]> git.ipfire.org Git - thirdparty/HylaFAX.git/commitdiff
Bug 451: enhance G3 encoder to support MMR,
authorLee Howard <faxguy@howardsilvan.com>
Mon, 1 Dec 2003 18:42:37 +0000 (18:42 +0000)
committerLee Howard <faxguy@howardsilvan.com>
Mon, 1 Dec 2003 18:42:37 +0000 (18:42 +0000)
         image tag lines on MMR faxes,
         implement ModemSoftRTFCC,
         consolidate MemoryDecoder code,
         fix RTFCC and tagtest.

15 files changed:
faxd/Class1.h
faxd/Class1Send.c++
faxd/Class2Send.c++
faxd/FaxModem.c++
faxd/FaxModem.h
faxd/FaxSend.c++
faxd/G3Encoder.c++
faxd/G3Encoder.h
faxd/MemoryDecoder.c++
faxd/MemoryDecoder.h
faxd/ModemConfig.c++
faxd/ModemConfig.h
faxd/TagLine.c++
faxd/tagtest.c++
man/hylafax-config.4f

index a2474d24e5c203ebea9c30e6353c46e9272f8e6f..9575cd01649b9853ddd3646ea0ea581ea72dde41 100644 (file)
@@ -106,7 +106,7 @@ protected:
     bool       raiseToNextBR(Class2Params&);
     bool       sendTraining(Class2Params&, int, fxStr& emsg);
     bool       sendTCF(const Class2Params&, u_int ms);
-    bool       sendPage(TIFF* tif, const Class2Params&, u_int, u_int, fxStr& emsg);
+    bool       sendPage(TIFF* tif, Class2Params&, u_int, u_int, fxStr& emsg);
     bool       sendPageData(u_char* data, u_int cc, const u_char* bitrev, bool ecm, fxStr& emsg);
     bool       sendRTC(Class2Params params, u_int ppmcmd, int lastbyte, fxStr& emsg);
     bool       sendPPM(u_int ppm, HDLCFrame& mcf, fxStr& emsg);
index f9b1e2c7228af720fa87091a686c061b203305d9..43f6e7f2195b6068bf065c9833c59df6dd33c566 100644 (file)
@@ -239,13 +239,15 @@ Class1Modem::sendPhaseB(TIFF* tif, Class2Params& next, FaxMachineInfo& info,
            params = next;
        }
 
-       /*
-        * According to T.30 5.3.2.4 we must pause at least 75 ms "after 
-        * receipt of a signal using the T.30 binary coded modulation" and 
-        * "before sending any signals using V.27 ter/V.29/V.33/V.17 
-        * modulation system"
-        */
-       pause(conf.class1SendMsgDelay);
+       if (params.ec != EC_ENABLE) {           // ECM does it later
+           /*
+            * According to T.30 5.3.2.4 we must pause at least 75 ms "after 
+            * receipt of a signal using the T.30 binary coded modulation" and 
+            * "before sending any signals using V.27 ter/V.29/V.33/V.17 
+            * modulation system"
+            */
+           pause(conf.class1SendMsgDelay);
+       }
 
        /*
         * The ECM protocol needs to know PPM, so this must be done beforehand...
@@ -836,6 +838,15 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
            // add one more flag to ensure one full flag gets transmitted before DLE+ETX
            blockData(0x7e, true);
 
+           // start up the high-speed carrier...
+           if (flowControl == FLOW_XONXOFF)   
+               setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
+           pause(conf.class1SendMsgDelay);             // T.30 5.3.2.4
+           fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
+           if (!atCmd(tmCmd, AT_CONNECT))
+               return (false);
+           pause(conf.class1TMConnectDelay);
+
            // The block is assembled.  Transmit it, adding transparent DLEs.  End with DLE+ETX.
            if (!putModemDLEData(ecmStuffedBlock, ecmStuffedBlockPos, bitrev, getDataTimeout())) {
                return (false);
@@ -1112,16 +1123,6 @@ Class1Modem::blockFrame(const u_char* bitrev, bool lastframe, u_int ppmcmd, fxSt
                protoTrace(emsg);
                return (false);
            }
-           if (!blockgood || !lastblock) {
-               // start up the high-speed carrier...
-               if (flowControl == FLOW_XONXOFF)   
-                   setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
-               pause(conf.class1SendMsgDelay);         // T.30 5.3.2.4
-               fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
-               if (!atCmd(tmCmd, AT_CONNECT))
-                   return (false);
-               pause(conf.class1TMConnectDelay);
-           }
        } while (!blockgood);
        frameNumber = 0;
        if (lastblock) blockNumber = 0;
@@ -1266,33 +1267,53 @@ EOLcode(u_long& w)
  * Send a page of data.
  */
 bool
-Class1Modem::sendPage(TIFF* tif, const Class2Params& params, u_int pageChop, u_int ppmcmd, fxStr& emsg)
+Class1Modem::sendPage(TIFF* tif, Class2Params& params, u_int pageChop, u_int ppmcmd, fxStr& emsg)
 {
     int lastbyte = 0;
-    /*
-     * Set high speed carrier & start transfer.  If the
-     * negotiated modulation technique includes short
-     * training, then we use it here (it's used for all
-     * high speed carrier traffic other than the TCF).
-     */
-    fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
-    if (!atCmd(tmCmd, AT_CONNECT)) {
-       emsg = "Unable to establish message carrier";
-       return (false);
+    if (params.ec != EC_ENABLE) {      // ECM does it later
+       /*
+        * Set high speed carrier & start transfer.  If the
+        * negotiated modulation technique includes short
+        * training, then we use it here (it's used for all
+        * high speed carrier traffic other than the TCF).
+        */
+       fxStr tmCmd(curcap[HasShortTraining(curcap)].value, tmCmdFmt);
+       if (!atCmd(tmCmd, AT_CONNECT)) {
+           emsg = "Unable to establish message carrier";
+           return (false);
+       }
+       // As with TCF, T.31 8.3.3 requires the DCE to report CONNECT at the beginning
+       // of transmission of the training pattern rather than at the end.  We pause here
+       // to allow the remote's +FRM to result in CONNECT.
+       pause(conf.class1TMConnectDelay);
+       if (flowControl == FLOW_XONXOFF)
+           setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
     }
-    // As with TCF, T.31 8.3.3 requires the DCE to report CONNECT at the beginning
-    // of transmission of the training pattern rather than at the end.  We pause here
-    // to allow the remote's +FRM to result in CONNECT.
-    pause(conf.class1TMConnectDelay);
 
     bool rc = true;
     ecmBlockPos = ecmFramePos = ecmBitPos = ecmOnes = ecmByte = 0;
     protoTrace("SEND begin page");
-    if (flowControl == FLOW_XONXOFF)
-       setXONXOFF(FLOW_XONXOFF, FLOW_NONE, ACT_FLUSH);
 
     tstrip_t nstrips = TIFFNumberOfStrips(tif);
     if (nstrips > 0) {
+
+       /*
+        * RTFCC may mislead us here, so we temporarily
+        * adjust params.
+        */
+       Class2Params newparams = params;
+       uint16 compression;
+       TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
+       if (compression != COMPRESSION_CCITTFAX4) {  
+           uint32 g3opts = 0;
+           TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &g3opts);
+           if ((g3opts & GROUP3OPT_2DENCODING) == DF_2DMR)
+               params.df = DF_2DMR;
+           else
+               params.df = DF_1DMH;
+       } else
+           params.df = DF_2DMMR;
+
        /*
         * Correct bit order of data if not what modem expects.
         */
@@ -1303,7 +1324,7 @@ Class1Modem::sendPage(TIFF* tif, const Class2Params& params, u_int pageChop, u_i
        /*
         * Setup tag line processing.
         */
-       bool doTagLine = (params.df != DF_2DMMR) && setupTagLineSlop(params);
+       bool doTagLine = setupTagLineSlop(params);
        u_int ts = getTagLineSlop();
        /*
         * Calculate total amount of space needed to read
@@ -1326,16 +1347,40 @@ Class1Modem::sendPage(TIFF* tif, const Class2Params& params, u_int pageChop, u_i
                off += (u_int) sbc;
        }
        totdata -= pageChop;            // deduct trailing white space not sent
+       uint32 rowsperstrip;
+       TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+       if (rowsperstrip == (uint32) -1)
+           TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
+
        /*
         * Image the tag line, if intended.
         */
        u_char* dp;
        if (doTagLine) {
-           dp = imageTagLine(data+ts, fillorder, params);
-           totdata = totdata+ts - (dp-data);
+           u_long totbytes = totdata;
+           dp = imageTagLine(data+ts, fillorder, params, totbytes);
+           // Because the whole image is processed with MMR, 
+           // totdata is then determined during encoding.
+           totdata = (params.df == DF_2DMMR) ? totbytes : totdata+ts - (dp-data);
        } else
            dp = data;
 
+       if (conf.softRTFCC && params.df != newparams.df) {
+           switch (params.df) {
+               case DF_1DMH:
+                   protoTrace("Reading MH-compressed image file");
+                   break;
+               case DF_2DMR:
+                   protoTrace("Reading MR-compressed image file");
+                   break;
+               case DF_2DMMR:
+                   protoTrace("Reading MMR-compressed image file");
+                   break;
+           }
+           dp = convertPhaseCData(dp, totdata, fillorder, params, newparams);
+       }
+       params = newparams;             // revert back
+
         /*
          * correct broken Phase C (T.4/T.6) data if neccessary 
          */
@@ -1363,10 +1408,6 @@ Class1Modem::sendPage(TIFF* tif, const Class2Params& params, u_int pageChop, u_i
             * bytes of data to send.  To minimize underrun we
             * do this padding in a strip-sized buffer.
             */
-           uint32 rowsperstrip;
-           TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
-           if (rowsperstrip == (uint32) -1)
-                TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
            u_char* fill = new u_char[minLen*rowsperstrip];
            u_char* eoFill = fill + minLen*rowsperstrip;
            u_char* fp = fill;
index 4658fff7a9831f23c6280ca3d76330eba4722f49..2a654ec309eaf0b1285d63eb08f0b4362344eb0b 100644 (file)
@@ -358,7 +358,7 @@ Class2Modem::sendPageData(TIFF* tif, u_int pageChop)
         * RTFCC may mislead us here, so we temporarily
         * adjust params.
         */
-       uint16 senddf = params.df;
+       Class2Params newparams = params;
        uint16 compression;
        TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
        if (compression != COMPRESSION_CCITTFAX4) {
@@ -381,12 +381,8 @@ Class2Modem::sendPageData(TIFF* tif, u_int pageChop)
        /*
         * Setup tag line processing.
         */
-       u_int ts = 0;
-       bool doTagLine = 0;
-       if (compression != COMPRESSION_CCITTFAX4) {     // broken in G4
-           doTagLine = setupTagLineSlop(params);
-           ts = getTagLineSlop();
-       }
+       u_int ts = getTagLineSlop();
+       bool doTagLine = setupTagLineSlop(params);
        /*
         * Calculate total amount of space needed to read
         * the image into memory (in its encoded format).
@@ -408,6 +404,10 @@ Class2Modem::sendPageData(TIFF* tif, u_int pageChop)
                off += (u_int) sbc;
        }
        totdata -= pageChop;            // deduct trailing white space not sent
+       uint32 rowsperstrip;
+       TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);  
+       if (rowsperstrip == (uint32) -1)
+           TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
        /*
         * Image the tag line, if intended, and then
         * pass the data to the modem, filtering DLE's
@@ -415,11 +415,28 @@ Class2Modem::sendPageData(TIFF* tif, u_int pageChop)
         */
        u_char* dp;
        if (doTagLine) {
-           dp = imageTagLine(data+ts, fillorder, params);
-           totdata = totdata+ts - (dp-data);
+           u_long totbytes = totdata;
+           dp = imageTagLine(data+ts, fillorder, params, totbytes);
+           totdata = (params.df == DF_2DMMR) ? totbytes : totdata+ts - (dp-data);
        } else
            dp = data;
 
+       if (conf.softRTFCC && !conf.class2RTFCC && params.df != newparams.df) {
+           switch (params.df) {
+               case DF_1DMH:
+                   protoTrace("Reading MH-compressed image file");
+                   break;
+               case DF_2DMR:
+                   protoTrace("Reading MR-compressed image file");
+                   break;
+               case DF_2DMMR:
+                   protoTrace("Reading MMR-compressed image file");
+                   break;
+           }
+           dp = convertPhaseCData(dp, totdata, fillorder, params, newparams);
+       }
+       params = newparams;             // revert back
+
         /*
          * correct broken Phase C (T.4/T.6) data if necessary
          */
@@ -429,9 +446,6 @@ Class2Modem::sendPageData(TIFF* tif, u_int pageChop)
        rc = putModemDLEData(dp, (u_int) totdata, bitrev, getDataTimeout());
        endTimedTransfer();
        protoTrace("SENT %u bytes of data", totdata);
-
-       params.df = senddf;             // revert back
-       delete data;
     }
     return (rc);
 }
index f1ff02d3a79dfa0f73e2f109a74778d69b8c5563..240d49833aaec137e02e822967e402b9cda411bf 100644 (file)
@@ -734,3 +734,13 @@ FaxModem::correctPhaseCData(u_char* buf, u_long* pBufSize,
         *pBufSize = endOfData - buf;
     return lastbyte;
 }
+
+u_char*
+FaxModem::convertPhaseCData(u_char* buf, u_long& totdata, u_int fillorder, 
+                           const Class2Params& params, const Class2Params& newparams)
+{
+    MemoryDecoder dec(buf, params.pageWidth(), totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
+    u_char* data = dec.convertDataFormat(newparams);
+    totdata = dec.getCC();
+    return (data);
+}
index 885d3d1ee336086b621d9a2c4f3118cc084e6727..abd2a9fb876d7697826425a1d0053c522b23dc31 100644 (file)
@@ -148,12 +148,17 @@ protected:
 // tag line support
     bool       setupTagLineSlop(const Class2Params&);
     u_int      getTagLineSlop() const;
-    u_char*    imageTagLine(u_char* buf, u_int fillorder, const Class2Params&);
+    u_char*    imageTagLine(u_char* buf, u_int fillorder, const Class2Params&, u_long& totdata);
 /*
  * Correct if neccessary Phase C (T.4/T.6) data (remove extra RTC/EOFB etc.)
  */
     int                correctPhaseCData(u_char* buf, u_long* pBufSize,
                                   u_int fillorder, const Class2Params& params);
+/*
+ * Convert Phase C data...
+ */
+    u_char*    convertPhaseCData(u_char* buf, u_long& totdata, u_int fillorder,
+                            const Class2Params& params, const Class2Params& newparams);
 public:
     enum {                     // FaxModem::RTNHandling
         RTN_RETRANSMIT = 0,         // retransmit page after RTN until MCF/MPS
index 4b5418f5fe757eaff3646f99883ac9c09aeaaff3..dd121ebb2979554185259cf397411f9392dc2a67 100644 (file)
@@ -520,11 +520,16 @@ FaxServer::sendSetupParams1(TIFF* tif,
      * re-reading the q file, we won't know if the data format was
      * requested.  So, RTFCC defeats requested data formatting. :-(
      */
-    if (class2RTFCC) {
+    if (class2RTFCC || softRTFCC) {
        params.df = clientCapabilities.df;
        // even if RTFCC supported uncompressed mode (and it doesn't)
        // it's likely that the remote was incorrect in telling us it does
        if (params.df == DF_2DMRUNCOMP) params.df = DF_2DMR;
+       // don't let RTFCC cause problems with restricted modems...
+       if (params.df == DF_2DMMR && (!modem->supportsMMR() || params.ec != EC_ENABLE))
+               params.df = DF_2DMR;
+       if (params.df == DF_2DMR && !modem->supports2D())
+               params.df = DF_1DMH;
     } else {
        if (compression == COMPRESSION_CCITTFAX4) {
            if (!clientInfo.getSupportsMMR()) {
index e08091b9404dc44eeabfb85d672f90876a9d8f22..f23a4e33a40f20d815a3bc4e92aac8f23be14867 100644 (file)
@@ -26,6 +26,8 @@
 
 /*
  * Group 3 Facsimile Writer Support.
+ *
+ * The 2-D encoding functionality was taken from the libtiff 3.5.7 distribution.
  */
 #include "G3Encoder.h"
 #include "StackBuffer.h"
@@ -50,6 +52,7 @@ G3Encoder::setupEncoder(u_int fillOrder, bool is2d, bool isg4)
     bitmap = TIFFGetBitRevTable(fillOrder != FILLORDER_MSB2LSB);
     data = 0;
     bit = 8;
+    firstEOL = true;
 }
 
 /*
@@ -63,41 +66,302 @@ G3Encoder::flushBits()
     bit = 8;
 }
 
+static const tableentry horizcode =
+    { 3, 0x1 };                /* 001 */
+static const tableentry passcode =
+    { 4, 0x1 };                /* 0001 */
+static const tableentry vcodes[7] = {   
+    { 7, 0x03 },       /* 0000 011 */
+    { 6, 0x03 },       /* 0000 11 */
+    { 3, 0x03 },       /* 011 */
+    { 1, 0x1 },                /* 1 */
+    { 3, 0x2 },                /* 010 */
+    { 6, 0x02 },       /* 0000 10 */
+    { 7, 0x02 }                /* 0000 010 */  
+};
+
+#define isAligned(p,t)  ((((u_long)(p)) & (sizeof (t)-1)) == 0)
+
 /*
- * Encode a multi-line raster.  We do everything with
- * 1D-data, inserting the appropriate tag bits when
- * 2D-encoding is required.
+ * Find a span of ones or zeros using the supplied
+ * table.  The byte-aligned start of the bit string
+ * is supplied along with the start+end bit indices.
+ * The table gives the number of consecutive ones or
+ * zeros starting from the msb and is indexed by byte
+ * value.
+ */
+int
+G3Encoder::findspan(const u_char** bpp, int bs, int be, const u_char* tab)
+{
+    const u_char *bp = *bpp;
+    int bits = be - bs;
+    int n, span;
+
+    /*
+     * Check partial byte on lhs.
+     */
+    if (bits > 0 && (n = (bs & 7))) {
+       span = tab[(*bp << n) & 0xff];
+       if (span > 8-n)        /* table value too generous */
+           span = 8-n;
+       if (span > bits)        /* constrain span to bit range */
+           span = bits;
+       if (n+span < 8)        /* doesn't extend to edge of byte */
+           goto done;
+       bits -= span;
+       bp++;
+    } else
+       span = 0;
+    /*
+     * Scan full bytes for all 1's or all 0's.
+     */
+    while (bits >= 8) {
+       n = tab[*bp];
+       span += n;
+       bits -= n;
+       if (n < 8)        /* end of run */
+           goto done;
+       bp++;
+    }
+    /*
+     * Check partial byte on rhs.
+     */
+    if (bits > 0) {
+       n = tab[*bp];
+       span += (n > bits ? bits : n);
+    }
+done:
+    *bpp = bp;
+    return (span);
+}
+
+/*
+ * Find a span of ones or zeros using the supplied
+ * table.  The ``base'' of the bit string is supplied
+ * along with the start+end bit indices.
+ */
+int
+G3Encoder::find0span(const u_char* bp, int bs, int be)
+{
+       int32 bits = be - bs;
+       int32 n, span;
+
+       bp += bs>>3;
+       /*
+        * Check partial byte on lhs.
+        */
+       if (bits > 0 && (n = (bs & 7))) {
+               span = zeroruns[(*bp << n) & 0xff];
+               if (span > 8-n)         /* table value too generous */
+                       span = 8-n;
+               if (span > bits)        /* constrain span to bit range */
+                       span = bits;
+               if (n+span < 8)         /* doesn't extend to edge of byte */
+                       return (span);
+               bits -= span;
+               bp++;
+       } else
+               span = 0;
+       if (bits >= 2*8*sizeof (long)) {
+               long* lp;
+               /*
+                * Align to longword boundary and check longwords.
+                */
+               while (!isAligned(bp, long)) {
+                       if (*bp != 0x00)
+                               return (span + zeroruns[*bp]);
+                       span += 8, bits -= 8;
+                       bp++;
+               }
+               lp = (long*) bp;
+               while (bits >= 8*sizeof (long) && *lp == 0) {
+                       span += 8*sizeof (long), bits -= 8*sizeof (long);
+                       lp++;
+               }
+               bp = (u_char*) lp;
+       }
+       /*
+        * Scan full bytes for all 0's.
+        */
+       while (bits >= 8) {
+               if (*bp != 0x00)        /* end of run */
+                       return (span + zeroruns[*bp]);
+               span += 8, bits -= 8;
+               bp++;
+       }
+       /*
+        * Check partial byte on rhs.
+        */
+       if (bits > 0) {
+               n = zeroruns[*bp];
+               span += (n > bits ? bits : n);
+       }
+       return (span);
+}
+
+int
+G3Encoder::find1span(const u_char* bp, int bs, int be)
+{
+       int32 bits = be - bs;
+       int32 n, span;
+
+       bp += bs>>3;
+       /*
+        * Check partial byte on lhs.
+        */
+       if (bits > 0 && (n = (bs & 7))) {
+               span = oneruns[(*bp << n) & 0xff];
+               if (span > 8-n)         /* table value too generous */
+                       span = 8-n;
+               if (span > bits)        /* constrain span to bit range */
+                       span = bits;
+               if (n+span < 8)         /* doesn't extend to edge of byte */
+                       return (span);
+               bits -= span;
+               bp++;
+       } else
+               span = 0;
+       if (bits >= 2*8*sizeof (long)) {
+               long* lp;
+               /*
+                * Align to longword boundary and check longwords.
+                */
+               while (!isAligned(bp, long)) {
+                       if (*bp != 0xff)
+                               return (span + oneruns[*bp]);
+                       span += 8, bits -= 8;
+                       bp++;
+               }
+               lp = (long*) bp;
+               while (bits >= 8*sizeof (long) && *lp == ~0) {
+                       span += 8*sizeof (long), bits -= 8*sizeof (long);
+                       lp++;
+               }
+               bp = (u_char*) lp;
+       }
+       /*
+        * Scan full bytes for all 1's.
+        */
+       while (bits >= 8) {
+               if (*bp != 0xff)        /* end of run */
+                       return (span + oneruns[*bp]);
+               span += 8, bits -= 8;
+               bp++;
+       }
+       /*
+        * Check partial byte on rhs.
+        */
+       if (bits > 0) {
+               n = oneruns[*bp];
+               span += (n > bits ? bits : n);
+       }
+       return (span);
+}
+
+/*
+ * Return the offset of the next bit in the range
+ * [bs..be] that is different from the specified
+ * color.  The end, be, is returned if no such bit
+ * exists.
+ */
+#define finddiff(_cp, _bs, _be, _color) \
+       (_bs + (_color ? find1span(_cp,_bs,_be) : find0span(_cp,_bs,_be)))
+
+/*
+ * Like finddiff, but also check the starting bit
+ * against the end in case start > end. 
+ */
+#define finddiff2(_cp, _bs, _be, _color) \
+       (_bs < _be ? finddiff(_cp,_bs,_be,_color) : _be)
+
+/*
+ * Encode a multi-line raster.  For MH and MR we can do everything with
+ * 1D-data, if desired, inserting the appropriate tag bits in MR.  For 
+ * MMR we must do everything with 2D-data, thus when coding 2D-data a 
+ * reference line, rp, is required.
  */
 void
-G3Encoder::encode(const void* vp, u_int w, u_int h)
+G3Encoder::encode(const void* vp, u_int w, u_int h, u_char* rp)
 {
+#define PIXEL(buf,ix)   ((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1)
     u_int rowbytes = howmany(w, 8);
-    bool firstEOL = true;
+    const u_char* bp = (const unsigned char*) vp;
 
     while (h-- > 0) {
-       if( firstEOL )                                  // according to T.4 first EOL 
-           firstEOL = false;                           // should not be aligned
-       else if (bit != 4)
-           putBits(0, (bit < 4) ? bit+4 : bit-4);      // byte-align other EOLs
-       if (is2D)
-           putBits((EOL<<1)|1, 12+1);
-       else
-           putBits(EOL, 12);
-       int bs = 0, span;
-       const u_char* bp = (const u_char*) vp;
-       for (;;) {
-           span = findspan(&bp, bs, w, zeroruns);      // white span
-           putspan(span, TIFFFaxWhiteCodes);
-           bs += span;
-           if (bs >= w)
-               break;
-           span = findspan(&bp, bs, w, oneruns);       // black span
-           putspan(span, TIFFFaxBlackCodes);
-           bs += span;
-           if (bs >= w)
-               break;
+       if (!isG4) {                                            // put the EOL
+           if( firstEOL )                                      // according to T.4 first EOL 
+               firstEOL = false;                               // should not be aligned
+           else if (bit != 4)
+               putBits(0, (bit < 4) ? bit+4 : bit-4);          // byte-align other EOLs
+           if (is2D)
+               if (rp)
+                   putBits((EOL<<1)|0, 12+1);                  // T.4 4.2.2
+               else
+                   putBits((EOL<<1)|1, 12+1);
+           else
+               putBits(EOL, 12);
+       }
+       if (rp) {                                               // 2-D line
+           uint32 a0 = 0;
+           uint32 a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, w, 0));
+           uint32 b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, w, 0));
+           uint32 a2, b2;
+           for (;;) {
+               b2 = finddiff2(rp, b1, w, PIXEL(rp,b1));
+               if (b2 >= a1) {
+                   int32 d = b1 - a1;
+                   if (!(-3 <= d && d <= 3)) {         /* horizontal mode */
+                       a2 = finddiff2(bp, a1, w, PIXEL(bp,a1));
+                       putcode(horizcode);
+                       if (a0+a1 == 0 || PIXEL(bp, a0) == 0) {
+                           putspan(a1-a0, TIFFFaxWhiteCodes);
+                           putspan(a2-a1, TIFFFaxBlackCodes);
+                       } else {
+                           putspan(a1-a0, TIFFFaxBlackCodes);
+                           putspan(a2-a1, TIFFFaxWhiteCodes);
+                       }
+                       a0 = a2;
+                   } else {                            /* vertical mode */
+                       putcode(vcodes[d+3]);
+                       a0 = a1;
+                   }
+               } else {                                /* pass mode */
+                   putcode(passcode);
+                   a0 = b2;
+               }
+               if (a0 >= w)
+                   break;
+               a1 = finddiff(bp, a0, w, PIXEL(bp,a0));
+               b1 = finddiff(rp, a0, w, !PIXEL(bp,a0));
+               b1 = finddiff(rp, b1, w, PIXEL(bp,a0));
+           }
+           memcpy(rp, bp, rowbytes);
+           bp += rowbytes;                                     // advance raster row
+       } else {                                                // 1-D line
+           int bs = 0, span;
+           for (;;) {
+               span = findspan(&bp, bs, w, zeroruns);          // white span
+               putspan(span, TIFFFaxWhiteCodes);
+               bs += span;
+               if (bs >= w)
+                   break;
+               span = findspan(&bp, bs, w, oneruns);           // black span
+               putspan(span, TIFFFaxBlackCodes);
+               bs += span;
+               if (bs >= w)
+                   break;
+           }
        }
-       vp = (const u_char*)vp + rowbytes;
+    }
+#undef PIXEL
+}
+
+void
+G3Encoder::encoderCleanup()
+{
+    if (isG4) {
+       putBits(EOL, 12);
+       putBits(EOL, 12);
     }
     if (bit != 8)                                      // flush partial byte
        flushBits();
@@ -155,59 +419,6 @@ G3Encoder::putBits(u_int bits, u_int length)
        flushBits();
 }
 
-/*
- * Find a span of ones or zeros using the supplied
- * table.  The byte-aligned start of the bit string
- * is supplied along with the start+end bit indices.
- * The table gives the number of consecutive ones or
- * zeros starting from the msb and is indexed by byte
- * value.
- */
-int
-G3Encoder::findspan(const u_char** bpp, int bs, int be, const u_char* tab)
-{
-    const u_char *bp = *bpp;
-    int bits = be - bs;
-    int n, span;
-
-    /*
-     * Check partial byte on lhs.
-     */
-    if (bits > 0 && (n = (bs & 7))) {
-       span = tab[(*bp << n) & 0xff];
-       if (span > 8-n)        /* table value too generous */
-           span = 8-n;
-       if (span > bits)        /* constrain span to bit range */
-           span = bits;
-       if (n+span < 8)        /* doesn't extend to edge of byte */
-           goto done;
-       bits -= span;
-       bp++;
-    } else
-       span = 0;
-    /*
-     * Scan full bytes for all 1's or all 0's.
-     */
-    while (bits >= 8) {
-       n = tab[*bp];
-       span += n;
-       bits -= n;
-       if (n < 8)        /* end of run */
-           goto done;
-       bp++;
-    }
-    /*
-     * Check partial byte on rhs.
-     */
-    if (bits > 0) {
-       n = tab[*bp];
-       span += (n > bits ? bits : n);
-    }
-done:
-    *bpp = bp;
-    return (span);
-}
-
 const u_char G3Encoder::zeroruns[256] = {
     8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,    /* 0x00 - 0x0f */
     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,    /* 0x10 - 0x1f */
@@ -244,16 +455,3 @@ const u_char G3Encoder::oneruns[256] = {
     3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,    /* 0xe0 - 0xef */
     4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,    /* 0xf0 - 0xff */
 };
-
-/*
- * Return the offset of the next bit in the range
- * [bs..be] that is different from bs.  The end,
- * be, is returned if no such bit exists.
- */
-int
-G3Encoder::finddiff(const u_char* cp, int bs, int be)
-{
-    cp += bs >> 3;            /* adjust byte offset */
-    return (bs + findspan(&cp, bs, be,
-       (*cp & (0x80 >> (bs&7))) ? oneruns : zeroruns));
-}
index a6379ae336848e2053a13c0b7c42632ae95c5a36..d3a359953503eb02e24725a375f933c81a4198ba 100644 (file)
@@ -37,6 +37,7 @@ class G3Encoder {
 private:
     bool       is2D;           // data is to be 1d/2d-encoded
     bool       isG4;           // data is to be G4-encoded
+    bool       firstEOL;       // first EOL is not byte-aligned
     const u_char* bitmap;      // bit reversal table
     short      data;           // current input/output byte
     short      bit;            // current bit in input/output byte
@@ -46,7 +47,9 @@ private:
     static const u_char oneruns[256];
 
     static int findspan(const u_char**, int, int, const u_char*);
-    static int finddiff(const u_char*, int, int);
+    static int find0span(const u_char*, int, int);
+    static int find1span(const u_char*, int, int);
+    static int finddiff(const u_char*, int, int, int);
 
     void       putBits(u_int bits, u_int length);
     void       putcode(const tableentry& te);
@@ -57,6 +60,7 @@ public:
     virtual ~G3Encoder();
 
     void       setupEncoder(u_int fillOrder, bool, bool);
-    void       encode(const void* raster, u_int w, u_int h);
+    void       encode(const void* raster, u_int w, u_int h, u_char* rp = NULL);
+    void       encoderCleanup();
 };
 #endif /* _G3Encoder_ */
index 3e7e76791bc66672142271bb0498de760cf9950e..57dffed633394c9d5302f705b5099f5d63578ed3 100644 (file)
@@ -82,6 +82,24 @@ MemoryDecoder::getLastByte()
     return (*(endOfData - 1));
 }
 
+void
+MemoryDecoder::invalidCode(const char* type, int x)
+{
+    printf("Invalid %s code word, x %d\n", type, x);
+}        
+void
+MemoryDecoder::badPixelCount(const char* type, int got, int expected)  
+{
+    if (!seenRTC())
+       printf("Bad %s pixel count, got %d, expected %d\n",
+           type, got, expected);
+}
+void
+MemoryDecoder::badDecodingState(const char* type, int x)
+{
+    printf("Panic, bad %s decoding state, x %d\n", type, x);
+}
+
 static bool
 isBlank(tiff_runlen_t* runs, u_int rowpixels)
 {
@@ -178,13 +196,14 @@ void MemoryDecoder::fixFirstEOL()
         (void)decodeRow(rowBuf, width);
         /*
          * syncronize to the next EOL and calculate pointer to it
-         * (see detailed explanation of look_ahead in TagLine.c++)
+         * (see detailed explanation of look_ahead in encodeTagLine())
          */
         (void)isNextRow1D();
         u_int look_ahead = roundup(getPendingBits(),8) / 8;
         u_int decoded = current() - look_ahead - start;
 
         enc.encode(rowBuf, width, 1);
+       enc.encoderCleanup();
         u_int encoded = result.getLength();
             
         while( encoded < decoded ){
@@ -234,7 +253,7 @@ u_char* MemoryDecoder::cutExtraRTC()
     if(!RTCraised()) {
         /*
          * syncronize to the next EOL and calculate pointer to it
-         * (see detailed explanation of look_ahead in TagLine.c++)
+         * (see detailed explanation of look_ahead in encodeTagLine())
          */
         (void)isNextRow1D();
         u_int look_ahead = roundup(getPendingBits(),8) / 8;
@@ -274,3 +293,160 @@ u_char* MemoryDecoder::cutExtraEOFB()
        endOfData--;    // step back over the first byte of EOFB
     return endOfData;
 }
+
+u_char* MemoryDecoder::encodeTagLine(u_long* raster, u_int th, u_int slop)
+{
+    /*
+     * Decode (and discard) the top part of the page where
+     * the tag line is to be imaged.  Note that we assume
+     * the strip of raw data has enough scanlines in it
+     * to satisfy our needs (caller is responsible).
+     *
+     * ... and then...
+     *
+     * Encode the result according to the parameters of
+     * the outgoing page.  Note that the encoded data is
+     * written in the bit order of the page data since
+     * it must be merged back with it below.
+     */
+    fxStackBuffer result;
+    G3Encoder enc(result);
+    enc.setupEncoder(fillorder, is2D, isG4);
+
+    u_char* start = current();
+    decode(NULL, width, th);           // discard decoded data
+    if (!isG4) {
+       /*
+        * If the source is 2D-encoded and the decoding done
+        * above leaves us at a row that is 2D-encoded, then
+        * our re-encoding below will generate a decoding
+        * error if we don't fix things up.  Thus we discard
+        * up to the next 1D-encoded scanline.  (We could
+        * instead decode the rows and re-encoded them below
+        * but to do that would require decoding above instead
+        * of skipping so that the reference line for the
+        * 2D-encoded rows is available.)
+        */
+       u_int n;
+       for (n = 0; n < 4 && !isNextRow1D(); n++)
+           decodeRow(NULL, width);
+       th += n;                        // compensate for discarded rows
+       /*
+        * Things get tricky trying to identify the last byte in
+        * the decoded data that we want to replace.  The decoder
+        * must potentially look ahead to see the zeros that
+        * makeup the EOL that marks the end of the data we want
+        * to skip over.  Consequently current() must be
+        * adjusted by the look ahead, a factor of the number of
+        * bits pending in the G3 decoder's bit accumulator.
+        */
+       u_int look_ahead = roundup(getPendingBits(),8) / 8;
+       u_int decoded = current() - look_ahead - bp;
+       enc.encode(raster, width, th);
+       enc.encoderCleanup();
+       delete raster;
+       /*
+        * To properly join the newly encoded data and the previous
+        * data we need to insert two bytes of zero-fill prior to
+        * the start of the old data to ensure 11 bits of zero exist
+        * prior to the EOL code in the first line of data that
+        * follows what we skipped over above.  Note that this
+        * assumes the G3 decoder always stops decoding prior to
+        * an EOL code and that we've adjusted the byte count to the
+        * start of the old data so that the leading bitstring is
+        * some number of zeros followed by a 1.
+        */
+       result.put((char) 0);
+       result.put((char) 0);
+       /*
+        * Copy the encoded raster with the tag line back to
+        * the front of the buffer that was passed in.  The
+        * caller has preallocated a hunk of space for us to
+        * do this and we also reuse space occupied by the
+        * original encoded raster image.  If insufficient space
+        * exists for the newly encoded tag line, then we jam
+        * as much as will fit w/o concern for EOL markers;
+        * this will cause at most one bad row to be received
+        * at the receiver (got a better idea?).
+        */
+       u_int encoded = result.getLength();
+       if (encoded > slop + decoded)
+           encoded = slop + decoded;
+       u_char* dst = bp + (int)(decoded-encoded);
+       memcpy(dst, (const unsigned char*) result, encoded);
+       return (dst);
+    } else {
+       u_char* refrow = new u_char[byteWidth*sizeof(u_char)];  // reference row
+       memset(refrow, 0, byteWidth*sizeof(u_char));            // clear to white
+       enc.encode(raster, width, th, (unsigned char*) refrow);
+       /*
+        * refrow does not need to be altered now to match the 
+        * last line of raster because the raster contains MARGIN_BOT
+        * blank lines.
+        */
+       delete raster;
+       if (!RTCraised()) {
+           for (;;) {
+               (void) decodeRow(rowBuf, width);
+               if(seenRTC())
+                   break;
+               enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
+               memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
+           }
+       }
+       enc.encoderCleanup();
+       cc = result.getLength();
+       u_char* dst = new u_char[cc];
+       memcpy(dst, (const unsigned char*) result, cc);
+       return (dst);
+    }
+}
+
+u_char* MemoryDecoder::convertDataFormat(const Class2Params& params)
+{
+    /*
+     * Convert data to the format specified in params.  The decoder has already
+     * been set up, and we don't need to worry about decoder operation here.  
+     * These params are for the encoder to use.
+     */
+    fxStackBuffer result;
+    G3Encoder enc(result);
+    enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
+
+    u_char* refrow = new u_char[byteWidth*sizeof(u_char)];     // reference row
+    memset(refrow, 0, byteWidth*sizeof(u_char));               // clear to white
+
+    /*
+     * For MR we encode a 1-D or 2-D line according to T.4 4.2.1.
+     * We understand that "standard" resolution means VR_NORMAL.
+     */
+    u_short k = 0;
+
+    if (!RTCraised()) {
+       for (;;) {
+           (void) decodeRow(rowBuf, width);
+           if(seenRTC())
+               break;
+           // encode the line specific to the desired format
+           if (params.df == DF_2DMMR) {
+               enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
+           } else if (params.df == DF_2DMR) {
+               if (k) {
+                   enc.encode(rowBuf, width, 1, (unsigned char*) refrow);      // 2-D
+               } else {
+                   enc.encode(rowBuf, width, 1);                               // 1-D
+                   k = (params.vr == VR_NORMAL || params.vr == VR_200X100) ? 2 : 4;
+               }
+               k--;
+           } else {    // DF_1DMH
+               enc.encode(rowBuf, width, 1);
+           }
+           memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
+       }
+    }
+    enc.encoderCleanup();
+    cc = result.getLength();
+    u_char* dst = new u_char[cc];
+    memcpy(dst, (const unsigned char*) result, cc);
+    return (dst);
+}
index 676490663c825074bad3793bcfdf87310c416f6c..c233994455b1fa3fa4c9bb7cc6e5748bd3716a4a 100644 (file)
@@ -44,6 +44,9 @@ private:
     u_char*    rowBuf;
 
     int                decodeNextByte();
+    void       invalidCode(const char* type, int x);
+    void       badPixelCount(const char* type, int got, int expected);
+    void       badDecodingState(const char* type, int x);
 public:
     MemoryDecoder(u_char* data, u_long cc);
     MemoryDecoder(u_char* data, u_int wid, u_long n,
@@ -53,11 +56,14 @@ public:
     void fixFirstEOL();
     u_char* cutExtraRTC();
     u_char* cutExtraEOFB();
+    u_char* encodeTagLine (u_long* raster, u_int th, u_int slop);
+    u_char* convertDataFormat(const Class2Params& params);
     int                getLastByte();
 
     void scanPageForBlanks(u_int fillorder, const Class2Params& params);
     const u_char* getEndOfPage()                       { return endOfData; }
     u_int getLastBlanks()                              { return nblanks; }
+    u_long getCC()                                     { return cc; }
 };
 
 #endif
index a7cba2045316b11d2ec8833114845d8315765718..3d3346728fa5bc59db06a8a4595213324d5f8ca4 100644 (file)
@@ -239,6 +239,7 @@ ModemConfig::setupConfig()
     recvDataFormat     = DF_ALL;               // default to no transcoding
     rtnHandling         = FaxModem::RTN_RETRANSMIT; // retransmit until MCF/MPS
     saveUnconfirmedPages = true;               // keep unconfirmed pages
+    softRTFCC          = false;                // real-time fax comp. conv. (software)
 }
 
 void
@@ -579,6 +580,8 @@ ModemConfig::setConfigItem(const char* tag, const char* value)
        class2UseHex = getBoolean(value);
     else if (streq(tag, "class2rtfcc"))
        class2RTFCC = getBoolean(value);
+    else if (streq(tag, "modemsoftrtfcc"))
+       softRTFCC = getBoolean(value);
     else if (streq(tag, "saveunconfirmedpages"))
        saveUnconfirmedPages = getBoolean(value);
     else
index cfed041831ffe7c935515e0a42f82240d42f726a..59cbb5acc57c7b516898d667cf7c38b3ee38536c 100644 (file)
@@ -192,6 +192,7 @@ public:
     u_int      percentGoodLines;       // required % of good lines in page
     u_int      maxConsecutiveBadLines; // max consecutive bad lines in page
     u_int      minSpeed;               // minimum speed for fax transmits
+    bool       softRTFCC;              // real-time fax compression conversion (software)
     bool       waitForConnect;         // modem sends multiple answer responses
     fxStr      tagLineFmt;             // format string for tag lines
     fxStr      tagLineFontFile;        // font file for imaging tag lines
index a0e6841b0468452fb6f0aa19fbefe1df95e27cc1..4c8940f3f33949e5f6503a0525ffd7c5344e11e8 100644 (file)
@@ -25,7 +25,6 @@
  */
 #include "FaxServer.h"
 #include "PCFFont.h"
-#include "G3Encoder.h"
 #include "StackBuffer.h"
 #include "FaxFont.h"
 #include "FaxRequest.h"
@@ -115,27 +114,7 @@ FaxModem::setupTagLineSlop(const Class2Params& params)
     }
 }
 
-/*
- * A memory-based G3 decoder--does no checking for end-of-data; it
- * assumes there'll be enough to satisfy the decoding requests.
- */
-class TagLineMemoryDecoder : public G3Decoder {
-private:
-    const u_char* bp;
-    int decodeNextByte();
-public:
-    TagLineMemoryDecoder(const u_char* data);
-    ~TagLineMemoryDecoder();
-    const u_char* current()            { return bp; }
-};
-TagLineMemoryDecoder::TagLineMemoryDecoder(const u_char* data) : bp(data) {}
-TagLineMemoryDecoder::~TagLineMemoryDecoder()  {}
-int TagLineMemoryDecoder::decodeNextByte()     { return *bp++; }
-
-#ifdef roundup
-#undef roundup
-#endif
-#define        roundup(a,b)    ((((a)+((b)-1))/(b))*(b))
+#include "MemoryDecoder.h"
 
 /*
  * Image the tag line in place of the top few lines of the page
@@ -147,7 +126,7 @@ int TagLineMemoryDecoder::decodeNextByte()  { return *bp++; }
  * setup the current page number.
  */
 u_char*
-FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
+FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params, u_long& totdata)
 {
     u_int l;
     /*
@@ -202,7 +181,7 @@ FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
      *     because the number of consecutive 2D-encoded rows is bounded
      *     by the K parameter in the CCITT spec.
      */
-    u_int lpr = howmany(w,32);                         // longs/raster row
+    u_int lpr = howmany(w, sizeof(u_long)*8);          // longs/raster row
     u_long* raster = new u_long[(h+SLOP_LINES)*lpr];   // decoded raster
     memset(raster,0,(h+SLOP_LINES)*lpr*sizeof (u_long));// clear raster to white
     /*
@@ -234,44 +213,6 @@ FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
        (void) tagLineFont->imageText(tagField, (u_short*) raster, w, h,
            xoff, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOT);
     }
-    /*
-     * Decode (and discard) the top part of the page where
-     * the tag line is to be imaged.  Note that we assume
-     * the strip of raw data has enough scanlines in it
-     * to satisfy our needs (caller is responsible).
-     */
-    TagLineMemoryDecoder dec(buf);
-    dec.setupDecoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
-    tiff_runlen_t runs[2*4864];                // run arrays for cur+ref rows
-    dec.setRuns(runs, runs+4864, w);
-
-    dec.decode(NULL, w, th);           // discard decoded data
-    /*
-     * If the source is 2D-encoded and the decoding done
-     * above leaves us at a row that is 2D-encoded, then
-     * our re-encoding below will generate a decoding
-     * error if we don't fix things up.  Thus we discard
-     * up to the next 1D-encoded scanline.  (We could
-     * instead decode the rows and re-encoded them below
-     * but to do that would require decoding above instead
-     * of skipping so that the reference line for the
-     * 2D-encoded rows is available.)
-     */
-    u_int n;
-    for (n = 0; n < 4 && !dec.isNextRow1D(); n++)
-       dec.decodeRow(NULL, w);
-    th += n;                           // compensate for discarded rows
-    /*
-     * Things get tricky trying to identify the last byte in
-     * the decoded data that we want to replace.  The decoder
-     * must potentially look ahead to see the zeros that
-     * makeup the EOL that marks the end of the data we want
-     * to skip over.  Consequently dec.current() must be
-     * adjusted by the look ahead, a factor of the number of
-     * bits pending in the G3 decoder's bit accumulator.
-     */
-    u_int look_ahead = roundup(dec.getPendingBits(),8) / 8;
-    u_int decoded = dec.current() - look_ahead - buf;
 
     /*
      * Scale image data as needed (see notes above).
@@ -390,46 +331,8 @@ FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
        }
        memset(l2, 0, MARGIN_BOT*lpr*sizeof (u_long));
     }
-
-    /*
-     * Encode the result according to the parameters of
-     * the outgoing page.  Note that the encoded data is
-     * written in the bit order of the page data since
-     * it must be merged back with it below.
-     */
-    fxStackBuffer result;
-    G3Encoder enc(result);
-    enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
-    enc.encode(raster, w, th);
-    delete raster;
-    /*
-     * To properly join the newly encoded data and the previous
-     * data we need to insert two bytes of zero-fill prior to
-     * the start of the old data to ensure 11 bits of zero exist
-     * prior to the EOL code in the first line of data that
-     * follows what we skipped over above.  Note that this
-     * assumes the G3 decoder always stops decoding prior to
-     * an EOL code and that we've adjusted the byte count to the
-     * start of the old data so that the leading bitstring is
-     * some number of zeros followed by a 1.
-     */
-    result.put((char) 0);
-    result.put((char) 0);
-    /*
-     * Copy the encoded raster with the tag line back to
-     * the front of the buffer that was passed in.  The
-     * caller has preallocated a hunk of space for us to
-     * do this and we also reuse space occupied by the
-     * original encoded raster image.  If insufficient space
-     * exists for the newly encoded tag line, then we jam
-     * as much as will fit w/o concern for EOL markers;
-     * this will cause at most one bad row to be received
-     * at the receiver (got a better idea?).
-     */
-    u_int encoded = result.getLength();
-    if (encoded > tagLineSlop + decoded)
-       encoded = tagLineSlop + decoded;
-    u_char* dst = buf + (int)(decoded-encoded);
-    memcpy(dst, (const unsigned char*)result, encoded);
-    return (dst);
+    MemoryDecoder dec(buf, w, totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
+    u_char* encbuf = dec.encodeTagLine(raster, th, tagLineSlop);   
+    totdata = dec.getCC();
+    return (encbuf);
 }
index 9c60eb420abbef66e71fe57feda6a9d5203eae4b..366380c8fce9c17b9ea3c10fbbbe9473d8cf66cd 100644 (file)
@@ -31,8 +31,6 @@
 #include "Sys.h"
 
 #include "PCFFont.h"
-#include "G3Decoder.h"
-#include "G3Encoder.h"
 #include "StackBuffer.h"
 #include "FaxFont.h"
 #include "tiffio.h"
@@ -127,47 +125,7 @@ setupTagLineSlop(const Class2Params& params)
        return (false);
 }
 
-class MemoryDecoder : public G3Decoder {
-private:
-    const u_char* bp;
-    int                row;
-
-    int                decodeNextByte();
-
-    void       invalidCode(const char* type, int x);
-    void       badPixelCount(const char* type, int got, int expected);
-    void       badDecodingState(const char* type, int x);
-public:
-    MemoryDecoder(const u_char* data);
-    ~MemoryDecoder();
-    const u_char* current()                            { return bp; }
-
-    void setRowNum(int r)                              { row = r; }
-};
-MemoryDecoder::MemoryDecoder(const u_char* data)       { bp = data; }
-MemoryDecoder::~MemoryDecoder()                                {}
-int MemoryDecoder::decodeNextByte()                    { return *bp++; }
-void
-MemoryDecoder::invalidCode(const char* type, int x)
-{
-    printf("Invalid %s code word, row %lu, x %d\n", type, row, x);
-}
-void
-MemoryDecoder::badPixelCount(const char* type, int got, int expected)
-{
-    printf("Bad %s pixel count, row %lu, got %d, expected %d\n",
-       type, row, got, expected);
-}
-void
-MemoryDecoder::badDecodingState(const char* type, int x)
-{
-    printf("Panic, bad %s decoding state, row %lu, x %d\n", type, row, x);
-}
-
-#ifdef roundup
-#undef roundup
-#endif
-#define        roundup(a,b)    ((((a)+((b)-1))/(b))*(b))
+#include "MemoryDecoder.h"
 
 /*
  * Image the tag line in place of the top few lines of the page
@@ -179,7 +137,7 @@ MemoryDecoder::badDecodingState(const char* type, int x)
  * setup the current page number.
  */
 u_char*
-imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
+imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params, u_long& totdata)
 {
     u_int l;
     /*
@@ -228,7 +186,7 @@ imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
      * longs here to optimize the scaling done below for the
      * low res case.  This should satisfy the word-alignment.
      */
-    u_int lpr = howmany(w,32);                         // longs/raster row
+    u_int lpr = howmany(w,sizeof(u_long)*8);           // longs/raster row
     u_long* raster = new u_long[(h+SLOP_LINES)*lpr];   // decoded raster
     memset(raster,0,(h+SLOP_LINES)*lpr*sizeof (u_long));// clear raster to white
     /*
@@ -260,49 +218,6 @@ imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
        (void) tagLineFont->imageText(tagField, (u_short*) raster, w, h,
            xoff, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOT);
     }
-    /*
-     * Decode (and discard) the top part of the page where
-     * the tag line is to be imaged.  Note that we assume
-     * the strip of raw data has enough scanlines in it
-     * to satisfy our needs (caller is responsible).
-     */
-    MemoryDecoder dec(buf);
-    dec.setupDecoder(fillorder,  params.is2D(), (params.df == DF_2DMMR));
-    tiff_runlen_t runs[2*4864];                // run arrays for cur+ref rows
-    dec.setRuns(runs, runs+4864, w);
-
-    u_int row;
-    for (row = 0; row < th; row++) {
-       dec.setRowNum(row);
-       dec.decodeRow(NULL, w);
-    }
-    /*
-     * If the source is 2D-encoded and the decoding done
-     * above leaves us at a row that is 2D-encoded, then
-     * our re-encoding below will generate a decoding
-     * error if we don't fix things up.  Thus we discard
-     * up to the next 1D-encoded scanline.  (We could
-     * instead decode the rows and re-encoded them below
-     * but to do that would require decoding above instead
-     * of skipping so that the reference line for the
-     * 2D-encoded rows is available.)
-     */
-    for (; row < th+4 && !dec.isNextRow1D(); row++) {
-       dec.setRowNum(row);
-       dec.decodeRow(NULL, w);
-    }
-    th = row;                          // add in discarded rows
-    /*
-     * Things get tricky trying to identify the last byte in
-     * the decoded data that we want to replace.  The decoder
-     * must potentially look ahead to see the zeros that
-     * makeup the EOL that marks the end of the data we want
-     * to skip over.  Consequently dec.current() must be
-     * adjusted by the look ahead, a factor of the number of
-     * bits pending in the G3 decoder's bit accumulator.
-     */
-    u_int look_ahead = roundup(dec.getPendingBits(),8) / 8;
-    u_int decoded = dec.current() - look_ahead - buf;
 
     /*
      * Scale image data as needed (see notes above).
@@ -421,48 +336,10 @@ imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params)
        }
        memset(l2, 0, MARGIN_BOT*lpr*sizeof (u_long));
     }
-
-    /*
-     * Encode the result according to the parameters of
-     * the outgoing page.  Note that the encoded data is
-     * written in the bit order of the page data since
-     * it must be merged back with it below.
-     */
-    fxStackBuffer result;
-    G3Encoder enc(result);
-    enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));
-    enc.encode(raster, w, th);
-    delete raster;
-    /*
-     * To properly join the newly encoded data and the previous
-     * data we need to insert two bytes of zero-fill prior to
-     * the start of the old data to ensure 11 bits of zero exist
-     * prior to the EOL code in the first line of data that
-     * follows what we skipped over above.  Note that this
-     * assumes the G3 decoder always stops decoding prior to
-     * an EOL code and that we've adjusted the byte count to the
-     * start of the old data so that the leading bitstring is
-     * some number of zeros followed by a 1.
-     */
-    result.put((char) 0);
-    result.put((char) 0);
-    /*
-     * Copy the encoded raster with the tag line back to
-     * the front of the buffer that was passed in.  The
-     * caller has preallocated a hunk of space for us to
-     * do this and we also reuse space occupied by the
-     * original encoded raster image.  If insufficient space
-     * exists for the newly encoded tag line, then we jam
-     * as much as will fit w/o concern for EOL markers;
-     * this will cause at most one bad row to be received
-     * at the receiver (got a better idea?).
-     */
-    u_int encoded = result.getLength();
-    if (encoded > tagLineSlop + decoded)
-       encoded = tagLineSlop + decoded;
-    u_char* dst = buf + (int)(decoded-encoded);
-    memcpy(dst, (const unsigned char*)result, encoded);
-    return (dst);
+    MemoryDecoder dec(buf, w, totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
+    u_char* encbuf = dec.encodeTagLine(raster, th, tagLineSlop);
+    totdata = dec.getCC();
+    return (encbuf);
 }
 
 void
@@ -603,17 +480,24 @@ main(int argc, char* argv[])
        TIFFSetField(otif, TIFFTAG_BITSPERSAMPLE, 1);
        TIFFSetField(otif, TIFFTAG_SAMPLESPERPIXEL, 1);
        TIFFSetField(otif, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
-       TIFFSetField(otif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
        TIFFSetField(otif, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
        uint32 r;
        TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &r);
        TIFFSetField(otif, TIFFTAG_ROWSPERSTRIP, r);
        TIFFSetField(otif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
        TIFFSetField(otif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+
+       TIFFSetField(otif, TIFFTAG_COMPRESSION, comp);
        uint32 opts = 0;
-       TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &opts);
-       params.df = (opts & GROUP3OPT_2DENCODING) ? DF_2DMR : DF_1DMH;
-       TIFFSetField(otif, TIFFTAG_GROUP3OPTIONS, opts);
+       if (comp != COMPRESSION_CCITTFAX4) {
+           TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &opts);
+           params.df = (opts & GROUP3OPT_2DENCODING) ? DF_2DMR : DF_1DMH;
+           TIFFSetField(otif, TIFFTAG_GROUP3OPTIONS, opts);
+       } else {
+           TIFFGetField(tif, TIFFTAG_GROUP4OPTIONS, &opts);
+            params.df = DF_2DMMR;
+           TIFFSetField(tif, TIFFTAG_GROUP4OPTIONS, opts);
+       }
        uint16 o;
        if (TIFFGetField(otif, TIFFTAG_ORIENTATION, &o))
            TIFFSetField(tif, TIFFTAG_ORIENTATION, o);
@@ -626,17 +510,22 @@ main(int argc, char* argv[])
        bool firstStrip = setupTagLineSlop(params);
        u_int ts = tagLineSlop;
        for (u_int strip = 0; strip < TIFFNumberOfStrips(tif); strip++) {
-           u_int totbytes = (u_int) stripbytecount[strip];
+           u_long totbytes = (u_long) stripbytecount[strip];
            if (totbytes > 0) {
                u_char* data = new u_char[totbytes+ts];
                if (TIFFReadRawStrip(tif, strip, data+ts, totbytes) >= 0) {
                    u_char* dp;
                    if (firstStrip) {
+                       uint32 rowsperstrip;
+                       TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);  
+                       if (rowsperstrip == (uint32) -1)
+                           TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &rowsperstrip);
                        /*
                         * Generate tag line at the top of the page.
                         */
-                       dp = imageTagLine(data+ts, fillorder, params);
-                       totbytes = totbytes+ts - (dp-data);
+                       u_long totdata = totbytes;
+                       dp = imageTagLine(data+ts, fillorder, params, totdata);
+                       totbytes = (params.df == DF_2DMMR) ? totdata : totbytes+ts - (dp-data);
                        firstStrip = false;
                    } else
                        dp = data;
index e0fe758939ff676ee892464b2098a832ef3121eb..632c6254236faeb8350ce7a37285ee2a5a9547da 100644 (file)
@@ -244,6 +244,7 @@ ModemSetupDTRCmd    string  \-      command for setting up \s-1DTR\s+1 handling
 ModemSoftFlowCmd       string  \-      command for setting software flow control between \s-1DTE\s+1 and \s-1DCE\s+1
 ModemSoftResetCmd      string  \s-1ATZ\s+1     command for doing a soft reset
 ModemSoftResetCmdDelay integer \s-13000\s+1    time, in ms, to pause after a soft reset
+ModemSoftRTFCC boolean \s-1No\s+1      enable software-driven real-time fax compression conversion
 ModemType      string  \s-1\fIsee below\fP\s+1 modem type
 ModemVerboseResultsCmd string  \s-1ATV1\s+1    command for enabling verbose result codes
 ModemWaitForConnect    boolean \s-1No\s+1      force server to wait for ``\s-1CONNECT\s+1'' response on answer
@@ -1942,6 +1943,21 @@ The time, in milliseconds, to pause after receiving the OK following
 .B ModemSoftResetCmd
 before any further commands are sent to the modem.
 .TP
+.B ModemSoftRTFCC
+Whether or not to enable software-driven Real-Time Fax Compression Conversion.
+RTFCC allows HylaFAX to convert the image compression between MH MR and MMR 
+formats regardless of how faxq formatted the image file.
+Note that when using RTFCC, the
+compression format of the file will be ignored, thus the ``-1'', ``-2'',
+and ``-3'' options for sendfax, ps2fax, and others will only influence how the
+document is prepared by faxq and will not influence the
+actual negotiated session parameters.
+.B Class2RTFCC
+takes precedence over
+.B ModemSoftRTFCC
+and if both are enabled, then software-driven RTFCC will not be performed
+in favor of the firmware-driven RTFCC.
+.TP
 .B ModemType
 This parameter must be set to one of: ``Class2'', ``Class2.0'',
 or ``Class1'';