about summary refs log tree commit diff
path: root/editor/pbmpscale.c
diff options
context:
space:
mode:
Diffstat (limited to 'editor/pbmpscale.c')
-rw-r--r--editor/pbmpscale.c554
1 files changed, 404 insertions, 150 deletions
diff --git a/editor/pbmpscale.c b/editor/pbmpscale.c
index 2e24f3cd..acdb862c 100644
--- a/editor/pbmpscale.c
+++ b/editor/pbmpscale.c
@@ -3,22 +3,63 @@
  */
 
 #include <stdio.h>
-#include "pbm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pbm.h"
+#include "bitarith.h"
 
-/* prototypes */
-void nextrow_pscale ARGS((FILE *ifd, int row));
-int corner ARGS((int pat));
+#define LEFTBITS pm_byteLeftBits
+#define RIGHTBITS pm_byteRightBits
 
-/* input bitmap size and storage */
-int rows, columns, format ;
-bit *inrow[3] ;
+/* Table for translating bit pattern into "corners" flag element */ 
 
-#define thisrow (1)
+unsigned char const
+transTable[512] = {
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+     0x00, 0x00, 0x04, 0x04, 0xaa, 0xa2, 0x82, 0x82, 0x8a, 0x82, 0x82, 0x82,
+     0xa0, 0xa0, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x10, 0x10,
+     0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x28, 0x28,
+     0x0a, 0x03, 0x01, 0x03, 0x0a, 0x03, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0xa8, 0xa0, 0xc0, 0xc0,
+     0x88, 0x80, 0x80, 0x80, 0xa0, 0xa0, 0xc0, 0xc0, 0x80, 0x80, 0x80, 0x80,
+     0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28,
+     0x00, 0x00, 0x28, 0x28, 0x0c, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a, 0x01, 0x01, 0x0a, 0x0a,
+     0x28, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0xa0, 0xa0,
+     0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
+     0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a,
+     0x01, 0x01, 0x0a, 0x0a, 0x28, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+     0x10, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0,
+     0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff,
+     0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0x2a, 0x22, 0x03, 0x02,
+     0x0a, 0x02, 0x03, 0x02, 0x30, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28,
+     0x00, 0x00, 0x28, 0x28, 0x0a, 0x02, 0x03, 0x02, 0x0a, 0x02, 0x03, 0x02,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04,
+     0x28, 0x20, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x30, 0x20, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,
+     0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x28, 0x28, 0x0c, 0xff, 0xff, 0xff,
+     0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a,
+     0x01, 0x01, 0x0a, 0x0a, 0x28, 0x20, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+     0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0,
+     0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff,
+     0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0x01, 0x01, 0x0a, 0x0a, 0x01, 0x01, 0x0a, 0x0a, 0x28, 0x20, 0x00, 0x00,
+     0x08, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x40, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff,
+     0x82, 0x82, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  };
 
-/* compass directions from west clockwise */
-int xd_pscale[] = { -1, -1,  0,  1, 1, 1, 0, -1 } ;
-int yd_pscale[] = {  0, -1, -1, -1, 0, 1, 1,  1 } ;
 
 /* starting positions for corners */
 #define NE(f) ((f) & 3)
@@ -26,180 +67,393 @@ int yd_pscale[] = {  0, -1, -1, -1, 0, 1, 1,  1 } ;
 #define SW(f) (((f) >> 4) & 3)
 #define NW(f) (((f) >> 6) & 3)
 
-typedef unsigned short sixteenbits ;
 
-/* list of corner patterns; bit 7 is current color, bits 0-6 are squares
- * around (excluding square behind), going clockwise.
- * The high byte of the patterns is a mask, which determines which bits are
- * not ignored.
- */
 
-sixteenbits patterns[] = { 0x0000, 0xd555,         /* no corner */
-                           0x0001, 0xffc1, 0xd514, /* normal corner */
-                           0x0002, 0xd554, 0xd515, /* reduced corners */
-                           0xbea2, 0xdfc0, 0xfd81,
-                           0xfd80, 0xdf80,
-                           0x0003, 0xbfa1, 0xfec2 /* reduced if > 1 */
-                           };
-
-/* search for corner patterns, return type of corner found:
- *  0 = no corner,
- *  1 = normal corner,
- *  2 = reduced corner,
- *  3 = reduced if cutoff > 1
- */
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int scale;
+    const char * inputFileName;  /* File name of input file */
+};
+
+
+
+static void
+parseCommandLine(int argc, const 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 OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1)
+        pm_error("You must specify the scale factor as an argument");
+    else {
+        int const scale = atoi(argv[1]);
+        if (scale < 1)
+            pm_error("Scale argument must be at least one.  You specified %d",
+                     scale);
+        else
+            cmdlineP->scale = scale;
 
-int corner(pat)
-     int pat;
-{
-   register int i, r=0;
-   for (i = 0; i < sizeof(patterns)/sizeof(sixteenbits); i++)
-      if (patterns[i] < 0x100)
-         r = patterns[i];
-      else if ((pat & (patterns[i] >> 8)) ==
-               (patterns[i] & (patterns[i] >> 8)))
-         return r;
-   return 0;
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  The only arguments are the "
+                         "scale factor and optional input file name.  "
+                         "You specified %u", argc-1);
+        }
+    }
 }
 
-/* get a new row
- */
 
-void nextrow_pscale(ifd, row)
-     FILE *ifd;
-     int row;
-{
-   bit *shuffle = inrow[0] ;
-   inrow[0] = inrow[1];
-   inrow[1] = inrow[2];
-   inrow[2] = shuffle ;
-   if (row < rows) {
-      if (shuffle == NULL)
-         inrow[2] = shuffle = pbm_allocrow(columns);
-      pbm_readpbmrow(ifd, inrow[2], columns, format) ;
-   } else inrow[2] = NULL; /* discard storage */
 
+static void
+validateComputableDimensions(unsigned int const width,
+                             unsigned int const height,
+                             unsigned int const scaleFactor) {
+/*----------------------------------------------------------------------------
+   Make sure that multiplication for output image width and height do not
+   overflow.
+   See validateComputetableSize() in libpam.c
+   and pbm_readpbminitrest() in libpbm2.c
+-----------------------------------------------------------------------------*/
+    unsigned int const maxWidthHeight = INT_MAX - 2;
+    unsigned int const maxScaleFactor = maxWidthHeight / MAX(height, width);
+
+    if (scaleFactor > maxScaleFactor)
+       pm_error("Scale factor '%u' too large.  "
+                "The maximum for this %u x %u input image is %u.",
+                scaleFactor, width, height, maxScaleFactor);
 }
 
 
 
-int
-main(int argc, char ** argv) {
+static void
+writeBitSpan(unsigned char * const packedBitrow,
+             int             const cols,
+             int             const offset,
+             int             const color) {
+/*----------------------------------------------------------------------------
+   Write white (color="0") or black (="1") bits into packedBitrow[],
+   starting at 'offset', length 'cols'.
+-----------------------------------------------------------------------------*/
+    unsigned char * const dest     = &packedBitrow[offset/8];
+    unsigned int    const rs       = offset % 8;
+    unsigned int    const trs      = (cols + rs) % 8;
+    unsigned int    const colBytes = pbm_packed_bytes(cols + rs);
+    unsigned int    const last     = colBytes - 1;
 
-    FILE * ifP;
-    bit * outrow;
-    unsigned int row;
-    int scale, cutoff, ucutoff ;
-    unsigned char *flags;
+    unsigned char const origHead = dest[0];
+    unsigned char const origEnd =  dest[last];
 
-    pbm_init( &argc, argv );
+    unsigned int i;
 
-    if (argc < 2)
-        pm_usage("scale [pbmfile]");
+    for (i = 0; i < colBytes; ++i)
+        dest[i] = color * 0xff;
 
-    scale = atoi(argv[1]);
-    if (scale < 1)
-        pm_error("Scale argument must be at least one.  You specified '%s'",
-                 argv[1]);
+    if (rs > 0)
+        dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
 
-    if (argc == 3)
-        ifP = pm_openr(argv[2]);
-    else
-        ifP = stdin ;
+    if (trs > 0)
+        dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
+}
 
-    inrow[0] = inrow[1] = inrow[2] = NULL;
-    pbm_readpbminit(ifP, &columns, &rows, &format) ;
 
-    outrow = pbm_allocrow(columns*scale) ;
-    MALLOCARRAY(flags, columns);
-    if (flags == NULL) 
-        pm_error("out of memory") ;
 
-    pbm_writepbminit(stdout, columns*scale, rows*scale, 0) ;
+static void
+setFlags(const bit *     const prevrow,
+         const bit *     const thisrow,
+         const bit *     const nextrow,
+         unsigned char * const flags,
+         unsigned int    const cols ) {
+/*----------------------------------------------------------------------------
+   Scan one row, examining the row above and row below, and determine 
+   whether there are "corners" for each pixel.  Feed a 9 bit sample into 
+   pre-calculated array transTable[512] to calculate all four corner statuses
+   at once.
 
-    cutoff = scale / 2;
-    ucutoff = scale - 1 - cutoff;
-    nextrow_pscale(ifP, 0);
-    for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        unsigned int i;
-        nextrow_pscale(ifP, row+1);
-        for (col = 0; col < columns; ++col) {
-            unsigned int i;
-            flags[col] = 0 ;
-            for (i = 0; i != 8; i += 2) {
-                int vec = inrow[thisrow][col] != PBM_WHITE;
-                unsigned int k;
-                for (k = 0; k < 7; ++k) {
-                    int x = col + xd_pscale[(k+i)&7] ;
-                    int y = thisrow + yd_pscale[(k+i)&7] ;
-                    vec <<= 1;
-                    if (x >=0 && x < columns && inrow[y])
-                        vec |= (inrow[y][x] != PBM_WHITE) ;
-                }
-                flags[col] |= corner(vec)<<i ;
-            }
+   Bits in the 9 bit sample represent the current pixel and neighbors:
+       NW : N : NE : W: Current : E : SW : S : SE
+
+   Bits in flag are divided into 4 fields of width 2 each:
+       NW : SW : SE : NE
+
+   Code 0xff is an exception.  It is a variation of 0x00.
+     0x00 : no corners, no color change from above row (Current == N)
+     0xff : no corners, but color changed (Current != N)
+
+   Most transTable[] entries are "no corners".
+   0x00 appears 180 times, 0xff 109 times.
+-----------------------------------------------------------------------------*/
+
+#if 0
+    /* The following code is from the previous version, which examined
+       the corners one by one:
+    */
+
+    /* list of corner patterns; bit 7 is current color, bits 0-6 are squares
+       around (excluding square behind), going clockwise.
+       The high byte of the patterns is a mask, which determines which bits are
+       not ignored.
+    */
+    uint16_t const patterns[] 
+        = { 0x0000,   0xd555,            /* no corner */
+            0x0001,   0xffc1, 0xd514,    /* normal corner */
+            0x0002,   0xd554, 0xd515, 0xbea2, 0xdfc0, 0xfd81, 0xfd80, 0xdf80,
+            /* reduced corners */
+            0x0003,   0xbfa1, 0xfec2 };  /* reduced if cutoff > 1 */
+
+    /*
+      For example, the NE corner is examined with the following 8 bit sample:
+      Current : W : NW : N : NE : E : SE : S
+      (SW is the "square behind") 
+      */
+#endif
+
+    uint32_t prevrow24, thisrow24, nextrow24;
+    unsigned int col;
+
+    /* higher bits are set to 0 */
+    prevrow24 = prevrow[0];  /* initial value */
+    thisrow24 = thisrow[0];  /* initial value */
+    nextrow24 = nextrow[0];  /* initial value */
+
+    for (col = 0; col < cols; ++col) {
+        unsigned int const col8   = col / 8;
+        unsigned int const offset = col % 8;
+
+        unsigned int sample;
+
+        if (offset == 0) {
+            prevrow24 = prevrow24 << 8 | prevrow[col8 + 1];
+            thisrow24 = thisrow24 << 8 | thisrow[col8 + 1];
+            nextrow24 = nextrow24 << 8 | nextrow[col8 + 1];
         }
-        for (i = 0; i < scale; i++) {
-            bit *ptr = outrow ;
-            int zone = (i > ucutoff) - (i < cutoff) ;
-            int cut = (zone < 0) ? (cutoff - i) :
-                (zone > 0) ? (i - ucutoff) : 0 ;
-
-            for (col = 0; col < columns; ++col) {
-                int pix = inrow[thisrow][col] ;
-                int flag = flags[col] ;
-                int cutl, cutr;
-                unsigned int k;
 
+        sample = ( ( prevrow24 >> ( 8 -offset) ) & 0x01c0 )
+            | ( ( thisrow24 >> (11 -offset) ) & 0x0038 )
+            | ( ( nextrow24 >> (14 -offset) ) & 0x0007 );
+        
+        flags[col] =  transTable[sample];
+    }
+}
+
+
+
+static void
+expandRow(const bit *     const thisrow,
+          const bit *     const prevrow,
+          bit *           const outrow,
+          unsigned char * const flags,
+          unsigned int    const cols,
+          int             const scale,
+          int             const cutoff,
+          int             const ucutoff) {
+/*----------------------------------------------------------------------------
+  Process one row, using flags array as reference.  If pixel has no corners
+  output a NxN square of the given color, otherwise output with the 
+  specified corner area(s) clipped off.
+-----------------------------------------------------------------------------*/
+    unsigned int const outcols = cols * scale;
+
+    unsigned int i;
+    unsigned int col;
+    
+    for (i = 0; i < scale; ++i) {
+        int const zone = (i > ucutoff) - (i < cutoff);
+        int const cut1 =
+            (zone < 0) ? (cutoff - i) : (zone > 0) ? (i - ucutoff) : 0;
+
+        unsigned int outcol;
+        int cut[4];
+
+        outcol = 0; /* initial value */
+
+        cut[0] = 0;
+        cut[1] = cut1;
+        cut[2] = cut1 ? cut1 - 1 : 0;
+        cut[3] = (cut1 && cutoff > 1) ? cut1 - 1 : cut1;
+      
+        for (col = 0; col < cols; ++col) {
+            unsigned int const col8 = col / 8;
+            unsigned int const offset = col % 8;
+            int const pix = (thisrow[col8] >> (7-offset) ) & 0x01;
+            int const flag = flags[col];
+
+            int cutl, cutr;
+
+            if (flag == 0x00) {
+                /* There are no corners, no color change */
+                outcol += scale;
+            } else { 
                 switch (zone) {
                 case -1:
-                    switch (NW(flag)) {
-                    case 0: cutl = 0; break;
-                    case 1: cutl = cut; break;
-                    case 2: cutl = cut ? cut-1 : 0; break;
-                    case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutl = 0;  /* Should never reach here */
-                    }
-                    switch (NE(flag)) {
-                    case 0: cutr = 0; break;
-                    case 1: cutr = cut; break;
-                    case 2: cutr = cut ? cut-1 : 0; break;
-                    case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutr = 0;  /* Should never reach here */
+                    if (i==0 && flag == 0xff) {
+                        /* No corners, color changed */ 
+                        cutl = cutr = 0;
+                        flags[col] = 0x00;
+                            /* Use above skip procedure next cycle */
+                    } else {
+                        cutl = cut[NW(flag)];
+                        cutr = cut[NE(flag)];
                     }
                     break;
                 case 0:
                     cutl = cutr = 0;
                     break ;
                 case 1:
-                    switch (SW(flag)) {
-                    case 0: cutl = 0; break;
-                    case 1: cutl = cut; break;
-                    case 2: cutl = cut ? cut-1 : 0; break;
-                    case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutl = 0;  /* should never reach here */
-                    }
-                    switch (SE(flag)) {
-                    case 0: cutr = 0; break;
-                    case 1: cutr = cut; break;
-                    case 2: cutr = cut ? cut-1 : 0; break;
-                    case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutr = 0;  /* should never reach here */
-                    }
+                    cutl = cut[SW(flag)];
+                    cutr = cut[SE(flag)];
                     break;
-                default: cutl = 0; cutr = 0;  /* Should never reach here */
                 }
-                for (k = 0; k < cutl; ++k) /* left part */
-                    *ptr++ = !pix ;
-                for (k = 0; k < scale-cutl-cutr; ++k)  /* center part */
-                    *ptr++ = pix ;
-                for (k = 0; k < cutr; ++k) /* right part */
-                    *ptr++ = !pix ;
+                
+                if (cutl > 0) {
+                    writeBitSpan(outrow, cutl, outcol, !pix);
+                    outcol += cutl;
+                }
+                {
+                    unsigned int const center = scale - cutl - cutr;
+
+                    if (center > 0) {
+                        writeBitSpan(outrow, center, outcol,  pix);
+                        outcol += center;
+                    }
+                }
+                if (cutr > 0) {
+                    writeBitSpan(outrow, cutr, outcol, !pix);
+                    outcol += cutr;
+                }
             }
-            pbm_writepbmrow(stdout, outrow, scale*columns, 0) ;
         }
+        pbm_writepbmrow_packed(stdout, outrow, outcols, 0) ;
+    }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    bit ** buffer;
+    bit * prevrow;
+    bit * thisrow;
+    bit * nextrow;
+    bit * edgerow;
+    bit * outrow;
+    unsigned int row;
+    unsigned int i;
+    int cols, rows;
+    int format;
+    unsigned int outcols;
+    unsigned int outrows;
+    int cutoff;
+    int ucutoff ;
+    unsigned char * flags;  /* malloc'ed */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format) ;
+
+    validateComputableDimensions(cols, rows, cmdline.scale); 
+
+    outcols = cols * cmdline.scale;
+    outrows = rows * cmdline.scale; 
+
+    /* Initialize input buffers.
+       We add a margin of 8 bits on the right of the three rows.
+
+       On the top and bottom of the image we place an imaginary blank row
+       ("edgerow") to facilitate the process.
+    */
+
+    buffer  = pbm_allocarray_packed(cols + 8, 3);
+    edgerow = pbm_allocrow_packed(cols + 8);
+
+    for (i = 0; i < pbm_packed_bytes(cols + 8); ++i)
+        edgerow[i] = 0x00;
+
+    /* Add blank bytes at right edges */ 
+    for (i = 0; i < 3; ++i)
+        buffer[i][pbm_packed_bytes(cols + 8) - 1] = 0x00;
+
+    thisrow = edgerow;
+    nextrow = buffer[0];
+
+    /* Read the top line into nextrow and clean the right end. */
+
+    pbm_readpbmrow_packed(ifP, nextrow, cols, format);
+
+    if (cols % 8 > 0) {
+        nextrow[pbm_packed_bytes(cols) - 1] >>= (8 - cols % 8);
+        nextrow[pbm_packed_bytes(cols) - 1] <<= (8 - cols % 8);
+    }
+
+    outrow = pbm_allocrow_packed(outcols);
+    for (i = 0; i < pbm_packed_bytes(outcols); ++i)
+        outrow[i] = 0x00;
+
+    MALLOCARRAY(flags, cols);
+    if (flags == NULL)
+        pm_error("Couldn't get memory for %u columns of flags", cols);
+
+    pbm_writepbminit(stdout, outcols, outrows, 0) ;
+
+    cutoff = cmdline.scale / 2;
+    ucutoff = cmdline.scale - 1 - cutoff;
+
+    for (row = 0; row < rows; ++row) {
+        prevrow = thisrow;  /* Slide up the input row window */
+        thisrow = nextrow;
+        if (row < rows - 1) {
+            nextrow = buffer[(row + 1) % 3];
+            /* We take the address directly instead of shuffling the rows.
+               This provision is for proper handling of the initial edgerow.
+            */
+            pbm_readpbmrow_packed(ifP, nextrow, cols, format);
+            if (cols % 8 > 0) {
+                nextrow[pbm_packed_bytes(cols) - 1] >>= (8 - cols % 8);
+                nextrow[pbm_packed_bytes(cols) - 1] <<= (8 - cols % 8);
+            }
+        } else
+            /* Bottom of image.  */
+            nextrow = edgerow;
+
+        setFlags(prevrow, thisrow, nextrow, flags, cols);
+
+        expandRow(thisrow, prevrow, outrow, flags, cols, cmdline.scale,
+                  cutoff, ucutoff);
     }
+    pbm_freearray(buffer,3);
+    pbm_freerow(edgerow);
+    pbm_freerow(outrow);
+    free (flags);
     pm_close(ifP);
     return 0;
 }