about summary refs log tree commit diff
path: root/converter/pbm/pbmtog3.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/pbm/pbmtog3.c')
-rw-r--r--converter/pbm/pbmtog3.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
new file mode 100644
index 00000000..10db3afe
--- /dev/null
+++ b/converter/pbm/pbmtog3.c
@@ -0,0 +1,485 @@
+/* pbmtog3.c - read a PBM image and produce a Group 3 FAX file
+**
+** Copyright (C) 1989 by Paul Haeberli <paul@manray.sgi.com>.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+/*
+   For specifications for Group 3 (G3) fax MH coding see ITU-T T.4
+   This program generates only MH.  It is coded with future expansion for
+   MR and MMR in mind.
+*/
+
+#include <assert.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "bitreverse.h"
+#include "wordaccess.h"
+#include "g3.h"
+
+#define TC_MC 64
+
+static bool const pbmtorl = 
+#ifdef PBMTORL
+    TRUE;
+#else
+    FALSE;
+#endif
+
+
+struct bitString {
+    /* A string of bits, up to as many fit in a word. */
+    unsigned int bitCount;
+        /* The length of the bit string */
+    wordint intBuffer;
+        /* The bits are in the 'bitCount' least significant bit positions 
+           of this number.  The rest of the bits of this number are always 
+           zero.
+        */
+
+    /* Example:  The bit string 010100, on a machine with a 32 bit word,
+       would be represented by bitCount = 6, intBuffer = 20
+       (N.B. 20 = 00000000 00000000 00000000 00010100 in binary)
+    */
+};
+
+
+
+struct outStream {
+    struct bitString buffer;
+    
+    bool reverseBits;
+};
+
+/* This is a global variable for speed. */
+static struct outStream out;
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    unsigned int reversebits;
+    unsigned int nofixedwidth;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.  */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
+            0);
+    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &cmdlineP->nofixedwidth,
+            0);
+    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
+            0);
+
+    /* TODO
+       Explicit fixed widths: -A4 -B4 -A3
+    */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFileName = argv[1];
+}
+
+
+
+static void
+reversebuffer(unsigned char * const p, 
+              unsigned int    const n) {
+
+    unsigned int i;
+    for (i = 0; i < n; ++i)
+        p[i] = bitreverse[p[i]];
+}
+
+
+
+static struct bitString
+makeBs(wordint      const bits, 
+       unsigned int const bitCount) {
+
+    struct bitString retval;
+    retval.intBuffer = bits;
+    retval.bitCount  = bitCount;
+
+    return retval;
+}
+
+    
+
+static __inline__ void
+putbits(struct bitString const newBits) {
+/*----------------------------------------------------------------------------
+   Push the bits 'newBits' onto the right end of output buffer
+   out.buffer (moving the bits already in the buffer left).
+
+   Flush the buffer to stdout as necessary to make room.
+
+   'newBits' must be shorter than a whole word.
+   
+   N.B. the definition of struct bitString requires upper bits to be zero.
+-----------------------------------------------------------------------------*/
+    unsigned int const spaceLeft = 
+        sizeof(out.buffer.intBuffer)*8 - out.buffer.bitCount;
+        /* Number of bits of unused space (at the high end) in buffer */
+
+    assert(newBits.bitCount < sizeof(out.buffer.intBuffer) * 8);
+    assert(newBits.intBuffer >> newBits.bitCount == 0);
+
+    if (spaceLeft > newBits.bitCount) {
+        /* New bits fit with bits to spare */
+        out.buffer.intBuffer = 
+            out.buffer.intBuffer << newBits.bitCount | newBits.intBuffer;
+        out.buffer.bitCount += newBits.bitCount;
+    } else { 
+        /* New bits fill buffer.  We'll have to flush the buffer to stdout
+           and put the rest of the bits in the new buffer.
+        */
+        unsigned int const nextBufBitCount = newBits.bitCount - spaceLeft;
+
+        wordintBytes outbytes;
+        size_t rc;
+
+        wordintToBytes(&outbytes, 
+                       (out.buffer.intBuffer << spaceLeft) 
+                       | (newBits.intBuffer >> nextBufBitCount));
+        if (out.reverseBits)
+            reversebuffer(outbytes, sizeof(outbytes));
+            
+        rc = fwrite(outbytes, 1, sizeof(outbytes), stdout);
+        if (rc != sizeof(outbytes))
+            pm_error("Output error.  Unable to fwrite() to stdout");
+        
+        out.buffer.intBuffer = newBits.intBuffer & ((1<<nextBufBitCount) - 1); 
+        out.buffer.bitCount = nextBufBitCount;
+    }
+}
+
+
+
+static void 
+initOutStream(bool const reverseBits) {
+    out.buffer.intBuffer = 0;
+    out.buffer.bitCount  = 0;
+    out.reverseBits = reverseBits;
+}
+
+
+
+static __inline__ void
+putcode(unsigned int const clr, 
+        unsigned int const ix) {
+
+    /* Note that this requires ttable to be aligned white entry, black
+       entry, white, black, etc.  
+    */
+    putbits(makeBs(ttable[ix * 2 + clr].code, ttable[ix * 2 + clr].length));
+}
+
+
+
+static __inline__ void
+putcode2(int const clr,
+         int const ix) {
+/*----------------------------------------------------------------------------
+   Output Make-up code and Terminating code at once.
+
+   For run lengths above TC_MC threshold (usually 64).
+
+   The codes are combined here to avoid calculations in putbits()
+   wordint is usually wide enough, with 32 or 64 bits.
+   Provisions are made for 16 bit wordint (for debugging).
+
+   Terminating code is max 12 bits, Make-up code is max 13 bits.
+   (See ttable, mtable entries in pbmtog3.h)
+
+   Also reduces object code size when putcode is compiled inline.
+-----------------------------------------------------------------------------*/
+    unsigned int const loIndex = ix % 64 * 2 + clr;
+    unsigned int const hiIndex = ix / 64 * 2 + clr;
+
+    if (sizeof(wordint) * 8 > 24) {
+        unsigned int const l1 = ttable[loIndex].length;
+        
+        putbits(
+            makeBs(mtable[hiIndex].code << l1 | ttable[loIndex].code,
+                   mtable[hiIndex].length + l1)
+            );
+    } else { /* typically 16 bit wordint used for debugging */
+        putbits(makeBs(mtable[hiIndex].code, mtable[hiIndex].length));
+        putbits(makeBs(ttable[loIndex].code, ttable[loIndex].length));
+    }
+}
+
+
+
+static __inline__ void
+putspan_normal(bit          const color, 
+               unsigned int const len) {
+
+    if (len < TC_MC)
+        putcode(color, len);
+    else if (len < 2624)
+        putcode2(color, len);
+    else {  /* len >= 2624 : rare */
+        unsigned int remainingLen;
+
+        for (remainingLen = len;
+             remainingLen >= 2624;
+             remainingLen -= 2623) {
+
+            putcode2(color, 2560+63);
+            putcode(!color, 0);
+        }
+        if (remainingLen < TC_MC)
+            putcode(color, remainingLen);
+        else  /* TC_MC <= len < 2624 */
+            putcode2(color, remainingLen);
+    }
+}
+
+
+
+static __inline__ void
+putspan(bit          const color, 
+        unsigned int const len) {
+/*----------------------------------------------------------------------------
+   Put a span of 'len' pixels of color 'color' in the output.
+-----------------------------------------------------------------------------*/
+    if (pbmtorl) {
+        if (len > 0) 
+            printf("%c %d\n", color == PBM_WHITE ? 'W' : 'B', len);
+    } else 
+        putspan_normal(color, len);
+}
+
+
+
+static void
+puteol(void) {
+
+    if (pbmtorl)
+        puts("EOL");
+    else {
+        struct bitString const eol = {12, 1};
+            
+        putbits(eol);
+    }
+}
+
+
+
+/*
+  PBM raw bitrow to inflection point array
+
+  Write inflection (=color change) points into array milepost[].  
+  It is easy to calculate run length from this.
+
+  In milepost, a white-to-black (black-to-white) inflection point
+  always has an even (odd) index.  A line starting with black is
+  indicated by bitrow[0] == 0.
+
+  WWWWWWWBBWWWWWWW ... = 7,2,7, ...
+  BBBBBWBBBBBWBBBB ... = 0,5,1,5,1,4, ...
+
+  Return the number of milepost elements written.
+  Note that max number of entries into milepost = cols+1 .
+
+  The inflection points are calculated like this:
+
+   r1: 00000000000111111110011111000000
+   r2: c0000000000011111111001111100000 0->carry
+  xor: ?0000000000100000001010000100000
+
+  The 1 bits in the xor above are the inflection points.
+*/
+
+static __inline__ void
+convertRowToRunLengths(unsigned char * const bitrow, 
+                       int             const cols, 
+                       unsigned int *  const milepost,
+                       unsigned int *  const lengthP) {
+
+    unsigned int   const bitsPerWord  = sizeof(wordint) * 8;
+    wordint      * const bitrowByWord = (wordint *) bitrow;
+    int            const wordCount    = (cols + bitsPerWord - 1)/bitsPerWord; 
+        /* Number of full and partial words in the row */
+        
+
+    if (cols % bitsPerWord != 0) {
+        /* Clean final word in row.  For loop simplicity */
+        wordint r1;
+        r1 = bytesToWordint((unsigned char *)&bitrowByWord[wordCount - 1]);
+        r1 >>= bitsPerWord - cols % bitsPerWord;
+        r1 <<= bitsPerWord - cols % bitsPerWord;
+        wordintToBytes((wordintBytes *)&bitrowByWord[wordCount - 1], r1);
+    }
+    {
+
+        wordint carry;
+        wordint r1, r2;
+        unsigned int n;
+        int i,c,k;
+
+        for (i = carry = n = 0; i < wordCount; ++i) {
+            r1 = r2 = bytesToWordint((unsigned char *)&bitrowByWord[i]);
+            r2 = r1 ^ (carry << (bitsPerWord-1) | r2 >> 1);
+            carry = r1 & 0x1;  k = 0;
+            while (r2 != 0) {
+                /* wordintClz(r2) reports most significant "1" bit of r2
+                   counting from MSB = position 0.
+                */
+                c = wordintClz(r2);
+                milepost[n++] = i * bitsPerWord + k + c;
+                r2 <<= c++; r2 <<= 1;  k += c; 
+            } 
+        }
+        if (milepost[n - 1] != cols) 
+            milepost[n++] = cols;
+        *lengthP = n;
+    }
+}
+
+
+
+static void
+padToDesiredWidth(unsigned int * const milepost,
+                  unsigned int * const nRunP,
+                  int            const existingCols,
+                  int            const desiredCols) {
+
+    if (existingCols < desiredCols) {
+        /* adjustment for narrow input in fixed width mode
+           nRun % 2 == 1 (0) means last (=rightmost) pixel is white (black)
+           if white, extend the last span to outwidth
+           if black, fill with a white span len (outwidth - readcols)
+        */
+        if (*nRunP % 2 == 0)
+            ++*nRunP;
+        milepost[*nRunP - 1] = desiredCols;
+    }
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    unsigned char * bitrow;
+       /* This is the bits of the current row, as read from the input and
+           modified various ways at various points in the program.  It has
+           a word of zero padding on the high (right) end for the convenience
+           of code that accesses this buffer in word-size bites.
+        */
+     
+    int rows;
+    int cols;
+    int readcols;
+    int outwidth;
+    int format;
+    int row;
+    unsigned int * milepost;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+     
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    if (cmdline.nofixedwidth)
+        readcols = outwidth = cols;
+    else {
+        readcols = MIN(cols, 1728);
+        outwidth = 1728;
+    }
+
+    MALLOCARRAY_NOFAIL(bitrow, pbm_packed_bytes(cols) + sizeof(wordint));
+
+    MALLOCARRAY_NOFAIL(milepost, readcols + 1);
+
+    initOutStream(cmdline.reversebits);
+    puteol();
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int nRun;  /* Number of runs in milepost[] */
+        unsigned int p;
+        unsigned int i;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        
+        convertRowToRunLengths(bitrow, readcols, milepost, &nRun);
+        
+        padToDesiredWidth(milepost, &nRun, readcols, outwidth);
+
+        for (i = p = 0; i < nRun; p = milepost[i++])
+            putspan(i%2 == 0 ? PBM_WHITE : PBM_BLACK, milepost[i] - p);
+        /* TODO 2-dimensional coding MR, MMR */
+        puteol();
+    }
+
+    free(milepost);
+    {
+        unsigned int i;  
+        for( i = 0; i < 6; ++i)
+            puteol();
+    }
+    if (out.buffer.bitCount > 0) {
+        /* flush final partial buffer */
+        unsigned int const bytesToWrite = (out.buffer.bitCount+7)/8;
+        
+        unsigned char outbytes[sizeof(wordint)];
+        size_t rc;
+        wordintToBytes(&outbytes, 
+                       out.buffer.intBuffer << (sizeof(out.buffer.intBuffer)*8 
+                                                - out.buffer.bitCount));
+        if (out.reverseBits)
+            reversebuffer(outbytes, bytesToWrite);
+        rc = fwrite(outbytes, 1, bytesToWrite, stdout);
+        if (rc != bytesToWrite)
+            pm_error("Output error");
+    }
+    pm_close(ifP);
+
+    return 0;
+}