about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2010-03-27 19:22:22 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2010-03-27 19:22:22 +0000
commit4c233fcfb54b386fcd96f71deb1e88e10d635825 (patch)
tree8cd7f623d29368a59905ccc108e5151bc28d2e62
parent3a54a339a59e83834ea18d88cab7104fd9d8b9ca (diff)
downloadnetpbm-mirror-4c233fcfb54b386fcd96f71deb1e88e10d635825.tar.gz
netpbm-mirror-4c233fcfb54b386fcd96f71deb1e88e10d635825.tar.xz
netpbm-mirror-4c233fcfb54b386fcd96f71deb1e88e10d635825.zip
Release 10.50.00 - copied from trunk
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@1162 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--analyzer/pnmhistmap.c330
-rw-r--r--converter/other/Makefile4
-rw-r--r--converter/other/avstopam.c103
-rw-r--r--converter/other/pamtoavs.c150
-rw-r--r--converter/other/pamtosvg/thin-image.c4
-rw-r--r--converter/other/pnmtosgi.c47
-rw-r--r--converter/other/ppmtopgm.c4
-rw-r--r--converter/pbm/pbmtogem.c252
-rw-r--r--converter/pbm/pbmtoybm.c173
-rw-r--r--converter/pbm/ybmtopbm.c174
-rw-r--r--converter/pgm/pgmtolispm.c224
-rw-r--r--converter/ppm/Makefile2
-rw-r--r--converter/ppm/ppmtompeg/jpeg.c2
-rw-r--r--doc/HISTORY65
-rw-r--r--doc/INSTALL7
-rw-r--r--editor/Makefile2
-rw-r--r--editor/pamcut.c20
-rw-r--r--editor/pamenlarge.c143
-rw-r--r--editor/pamflip.c40
-rw-r--r--editor/pampaintspill.c386
-rw-r--r--editor/pbmclean.c427
-rw-r--r--editor/pbmpscale.c554
-rw-r--r--editor/pnmconvol.c100
-rw-r--r--editor/pnmhisteq.c56
-rw-r--r--editor/pnmnorm.c4
-rw-r--r--editor/pnmremap.c3
-rw-r--r--editor/ppmdist.c2
-rw-r--r--generator/pbmpage.c251
-rw-r--r--lib/libpam.c61
-rw-r--r--lib/libpbmfont.c80
-rw-r--r--lib/libpgm1.c2
-rw-r--r--lib/libpnm3.c2
-rw-r--r--lib/libppmd.c214
-rw-r--r--lib/pam.h4
-rw-r--r--lib/pnm.h3
-rw-r--r--lib/ppm.h6
-rw-r--r--lib/util/shhopt.h12
-rw-r--r--netpbm.c8
-rw-r--r--version.mk4
39 files changed, 2792 insertions, 1133 deletions
diff --git a/analyzer/pnmhistmap.c b/analyzer/pnmhistmap.c
index 7e504734..c45ab0c6 100644
--- a/analyzer/pnmhistmap.c
+++ b/analyzer/pnmhistmap.c
@@ -17,6 +17,7 @@
  * - Deal properly with maxvals other than 256
  */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -26,8 +27,6 @@
 
 static double const epsilon = .00001;
 
-#define SCALE_H(value) (hscale_unity ? (value) : (int)((value) * hscale))
-
 enum wantedColor {WANT_RED=0, WANT_GRN=1, WANT_BLU=2};
 
 struct cmdlineInfo {
@@ -53,7 +52,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -93,7 +92,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!lvalSpec)
@@ -127,7 +126,7 @@ parseCommandLine(int argc, char ** argv,
 
 static unsigned int
 maxSlotCount(const unsigned int * const hist,
-             unsigned int         const hist_width,
+             unsigned int         const histWidth,
              bool                 const no_white,
              bool                 const no_black) {
 /*----------------------------------------------------------------------------
@@ -138,7 +137,7 @@ maxSlotCount(const unsigned int * const hist,
     unsigned int i;
 
     unsigned int const start = (no_black ? 1 : 0);
-    unsigned int const finish = (no_white ? hist_width - 1 : hist_width);
+    unsigned int const finish = (no_white ? histWidth - 1 : histWidth);
     for (hmax = 0, i = start; i < finish; ++i)
         if (hmax < hist[i])
             hmax = hist[i];
@@ -150,36 +149,52 @@ maxSlotCount(const unsigned int * const hist,
 
 static void
 clipHistogram(unsigned int * const hist,
-              unsigned int   const hist_width,
+              unsigned int   const histWidth,
               unsigned int   const hmax) {
 
     unsigned int i;
 
-    for (i = 0; i < hist_width; ++i)
+    for (i = 0; i < histWidth; ++i)
         hist[i] = MIN(hmax, hist[i]);
 }
 
 
 
 static void
-pgm_hist(FILE *       const ifP,
-         int          const cols,
-         int          const rows,
-         xelval       const maxval,
-         int          const format,
-         bool         const dots,
-         bool         const no_white,
-         bool         const no_black,
-         bool         const verbose,
-         xelval       const startval,
-         xelval       const endval,
-         unsigned int const hist_width,
-         unsigned int const hist_height,
-         bool         const clipSpec,
-         unsigned int const clipCount,
-         double       const hscale) {
-
-    bool const hscale_unity = hscale - 1 < epsilon;
+countComp(xelval         const value,
+          xelval         const startval,
+          xelval         const endval,
+          unsigned int   const histWidth,
+          unsigned int * const hist) {
+
+    double const hscale = (float)(histWidth-1) / (endval - startval - 1);
+
+    if (value >= startval && value < endval) {
+        unsigned int const bin = ROUNDU((value-startval) * hscale);
+
+        assert(bin < histWidth);
+        ++hist[bin];
+    }
+}
+
+
+
+static void
+pgmHist(FILE *       const ifP,
+        int          const cols,
+        int          const rows,
+        xelval       const maxval,
+        int          const format,
+        bool         const dots,
+        bool         const no_white,
+        bool         const no_black,
+        bool         const verbose,
+        xelval       const startval,
+        xelval       const endval,
+        unsigned int const histWidth,
+        unsigned int const histHeight,
+        bool         const clipSpec,
+        unsigned int const clipCount) {
 
     gray * grayrow;
     bit ** bits;
@@ -188,15 +203,15 @@ pgm_hist(FILE *       const ifP,
     double vscale;
     unsigned int hmax;
     
-    MALLOCARRAY(ghist, hist_width);
+    MALLOCARRAY(ghist, histWidth);
     if (ghist == NULL)
         pm_error("Not enough memory for histogram array (%d bytes)",
-                  hist_width * sizeof(int));
-    bits = pbm_allocarray(hist_width, hist_height);
+                  histWidth * sizeof(int));
+    bits = pbm_allocarray(histWidth, histHeight);
     if (bits == NULL)
         pm_error("no space for output array (%d bits)",
-                 hist_width * hist_height);
-    memset(ghist, 0, hist_width * sizeof(ghist[0]));
+                 histWidth * histHeight);
+    memset(ghist, 0, histWidth * sizeof(ghist[0]));
 
     /* read the pixel values into the histogram arrays */
     grayrow = pgm_allocrow(cols);
@@ -206,12 +221,8 @@ pgm_hist(FILE *       const ifP,
 
     for (i = rows; i > 0; --i) {
         pgm_readpgmrow (ifP, grayrow, cols, maxval, format);
-        for (j = cols-1; j >= 0; --j) {
-            int const value = grayrow[j];
-
-            if (value >= startval && value <= endval)
-                ++ghist[SCALE_H(value-startval)];
-        }
+        for (j = cols-1; j >= 0; --j)
+            countComp(grayrow[j], startval, endval, histWidth, ghist);
     }
     pgm_freerow(grayrow);
 
@@ -221,33 +232,35 @@ pgm_hist(FILE *       const ifP,
     if (clipSpec)
         hmax = clipCount;
     else 
-        hmax = maxSlotCount(ghist, hist_width, no_white, no_black);
+        hmax = maxSlotCount(ghist, histWidth, no_white, no_black);
+
+    assert(hmax > 0);
 
     if (verbose)
         pm_message("Done: height = %u", hmax);
 
-    clipHistogram(ghist, hist_width, hmax);
+    clipHistogram(ghist, histWidth, hmax);
 
-    vscale = (double) hist_height / hmax;
+    vscale = (double) histHeight / hmax;
 
-    for (i = 0; i < hist_width; ++i) {
-        int mark = hist_height - (int)(vscale * ghist[i]);
+    for (i = 0; i < histWidth; ++i) {
+        int mark = histHeight - (int)(vscale * ghist[i]);
         for (j = 0; j < mark; ++j)
             bits[j][i] = PBM_BLACK;
-        if (j < hist_height)
+        if (j < histHeight)
             bits[j++][i] = PBM_WHITE;
-        for ( ; j < hist_height; ++j)
+        for ( ; j < histHeight; ++j)
             bits[j][i] = dots ? PBM_BLACK : PBM_WHITE;
     }
 
-    pbm_writepbm(stdout, bits, hist_width, hist_height, 0);
+    pbm_writepbm(stdout, bits, histWidth, histHeight, 0);
 }
 
 
 
 static unsigned int
 maxSlotCountAll(unsigned int *       const hist[3],
-                unsigned int         const hist_width,
+                unsigned int         const histWidth,
                 bool                 const no_white,
                 bool                 const no_black) {
 /*----------------------------------------------------------------------------
@@ -264,7 +277,7 @@ maxSlotCountAll(unsigned int *       const hist[3],
         if (hist[color])
             hmax = MAX(hmax, 
                        maxSlotCount(hist[color], 
-                                    hist_width, no_white, no_black));
+                                    histWidth, no_white, no_black));
     
     return hmax;
 }
@@ -273,107 +286,132 @@ maxSlotCountAll(unsigned int *       const hist[3],
 
 static void
 createHist(bool             const colorWanted[3],
-           unsigned int     const hist_width,
+           unsigned int     const histWidth,
            unsigned int * (* const histP)[3]) {
 /*----------------------------------------------------------------------------
    Allocate the histogram arrays and set each slot count to zero.
 -----------------------------------------------------------------------------*/
     unsigned int color;
 
-    for (color = 0; color < 3; ++color)
+    for (color = 0; color < 3; ++color) {
         if (colorWanted[color]) {
             unsigned int * hist;
             unsigned int i;
-            MALLOCARRAY(hist, hist_width);
+            MALLOCARRAY(hist, histWidth);
             if (hist == NULL)
                 pm_error ("Not enough memory for histogram arrays (%u bytes)",
-                          hist_width * sizeof(int) * 3);
+                          histWidth * sizeof(hist[0]) * 3);
 
-            for (i = 0; i < hist_width; ++i)
+            for (i = 0; i < histWidth; ++i)
                 hist[i] = 0;
             (*histP)[color] = hist;
         } else
             (*histP)[color] = NULL;
+    }
 }
 
 
 
 static void
 clipHistogramAll(unsigned int * const hist[3],
-                 unsigned int   const hist_width,
+                 unsigned int   const histWidth,
                  unsigned int   const hmax) {
 
     unsigned int color;
 
     for (color = 0; color < 3; ++color)
         if (hist[color])
-            clipHistogram(hist[color], hist_width, hmax);
+            clipHistogram(hist[color], histWidth, hmax);
 }
 
 
 
 static void
-ppm_hist(FILE *       const ifP,
-         int          const cols,
-         int          const rows,
-         xelval       const maxval,
-         int          const format,
-         bool         const dots,
-         bool         const no_white,
-         bool         const no_black,
-         bool         const colorWanted[3],
-         bool         const verbose,
-         xelval       const startval,
-         xelval       const endval,
-         unsigned int const hist_width,
-         unsigned int const hist_height,
-         bool         const clipSpec,
-         unsigned int const clipCount,
-         double       const hscale) {
-
-    bool const hscale_unity = hscale - 1 < epsilon;
-
+fillPpmBins(FILE *          const ifP,
+            unsigned int    const cols,
+            unsigned int    const rows,
+            xelval          const maxval,
+            int             const format,
+            bool            const colorWanted[3],
+            bool            const verbose,
+            xelval          const startval,
+            xelval          const endval,
+            unsigned int    const histWidth,
+            unsigned int ** const hist) {
+/*----------------------------------------------------------------------------
+   For each wanted color component, given by colorWanted[], hist[color] is the
+   histogram.  Each histogram as 'histWidth' bins; we ignore color component
+   values less than 'startval' and greater than or equal to 'endval' and
+   spread the rest evenly across the 'histWidth' bins.
+
+   We get the color component values from the PNM image on *ifP,
+   which is positioned to the raster, whose format is described
+   by 'cols', 'rows', 'maxval', and 'format'.
+-----------------------------------------------------------------------------*/
     pixel * pixrow;
-    pixel ** pixels;
-    int i, j;
-    unsigned int * hist[3];  /* Subscript is enum wantedColor */
-    double vscale;
-    unsigned int hmax;
+    unsigned int row;
 
-    createHist(colorWanted, hist_width, &hist);
-
-    if ((pixels = ppm_allocarray (hist_width, hist_height)) == NULL)
-        pm_error("no space for output array (%d pixels)",
-                 hist_width * hist_height);
-    for (i = 0; i < hist_height; ++i)
-        memset(pixels[i], 0, hist_width * sizeof(pixels[i][0]));
-
-    /* read the pixel values into the histogram arrays */
     pixrow = ppm_allocrow(cols);
 
     if (verbose)
         pm_message("making histogram...");
 
-    for (i = rows; i > 0; --i) {
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
         ppm_readppmrow(ifP, pixrow, cols, maxval, format);
-        for (j = cols-1; j >= 0; --j) {
-            int value;
-
-            if (colorWanted[WANT_RED] && 
-                (value = PPM_GETR(pixrow[j])) >= startval && 
-                value <= endval)
-                hist[WANT_RED][SCALE_H(value-startval)]++;
-            if (colorWanted[WANT_GRN] && 
-                (value = PPM_GETG(pixrow[j])) >= startval && 
-                value <= endval)
-                hist[WANT_GRN][SCALE_H(value-startval)]++;
-            if (colorWanted[WANT_BLU] && 
-                (value = PPM_GETB(pixrow[j])) >= startval && 
-                value <= endval)
-                hist[WANT_BLU][SCALE_H(value-startval)]++;
+        for (col = 0; col < cols; ++col) {
+            if (colorWanted[WANT_RED])
+                countComp(PPM_GETR(pixrow[col]),
+                          startval, endval, histWidth, hist[WANT_RED]);
+
+            if (colorWanted[WANT_GRN])
+                countComp(PPM_GETG(pixrow[col]),
+                          startval, endval, histWidth, hist[WANT_GRN]);
+
+            if (colorWanted[WANT_BLU])
+                countComp(PPM_GETB(pixrow[col]),
+                          startval, endval, histWidth, hist[WANT_BLU]);
         }
     }
     ppm_freerow(pixrow);
+}
+
+
+
+static void
+ppmHist(FILE *       const ifP,
+        unsigned int const cols,
+        unsigned int const rows,
+        xelval       const maxval,
+        int          const format,
+        bool         const dots,
+        bool         const no_white,
+        bool         const no_black,
+        bool         const colorWanted[3],
+        bool         const verbose,
+        xelval       const startval,
+        xelval       const endval,
+        unsigned int const histWidth,
+        unsigned int const histHeight,
+        bool         const clipSpec,
+        unsigned int const clipCount) {
+
+    pixel ** pixels;
+    unsigned int i;
+    unsigned int * hist[3];  /* Subscript is enum wantedColor */
+    double vscale;
+    unsigned int hmax;
+
+    createHist(colorWanted, histWidth, &hist);
+
+    if ((pixels = ppm_allocarray (histWidth, histHeight)) == NULL)
+        pm_error("no space for output array (%u pixels)",
+                 histWidth * histHeight);
+    for (i = 0; i < histHeight; ++i)
+        memset(pixels[i], 0, histWidth * sizeof(pixels[i][0]));
+
+    fillPpmBins(ifP, cols, rows, maxval, format, colorWanted, verbose,
+                startval, endval, histWidth, hist);
 
     /* find the highest-valued slot and set the vertical scale value */
     if (verbose)
@@ -381,22 +419,24 @@ ppm_hist(FILE *       const ifP,
     if (clipSpec)
         hmax = clipCount;
     else 
-        hmax = maxSlotCountAll(hist, hist_width, no_white, no_black);
+        hmax = maxSlotCountAll(hist, histWidth, no_white, no_black);
 
-    clipHistogramAll(hist, hist_width, hmax);
+    assert(hmax > 0);
 
-    vscale = (double) hist_height / hmax;
+    clipHistogramAll(hist, histWidth, hmax);
+
+    vscale = (double) histHeight / hmax;
     if (verbose)
-        pm_message("Done: height = %d, vertical scale factor = %g", 
+        pm_message("Done: height = %u, vertical scale factor = %g", 
                    hmax, vscale);
 
-    for (i = 0; i < hist_width; ++i) {
+    for (i = 0; i < histWidth; ++i) {
         if (hist[WANT_RED]) {
             unsigned int j;
             bool plotted;
             plotted = FALSE;
-            for (j = hist_height - (int)(vscale * hist[WANT_RED][i]); 
-                 j < hist_height && !plotted; 
+            for (j = histHeight - (int)(vscale * hist[WANT_RED][i]); 
+                 j < histHeight && !plotted; 
                  ++j) {
                 PPM_PUTR(pixels[j][i], maxval);
                 plotted = dots;
@@ -406,8 +446,8 @@ ppm_hist(FILE *       const ifP,
             unsigned int j;
             bool plotted;
             plotted = FALSE;
-            for (j = hist_height - (int)(vscale * hist[WANT_GRN][i]); 
-                 j < hist_height && !plotted; 
+            for (j = histHeight - (int)(vscale * hist[WANT_GRN][i]); 
+                 j < histHeight && !plotted; 
                  ++j) {
                 PPM_PUTG(pixels[j][i], maxval);
                 plotted = dots;
@@ -417,33 +457,46 @@ ppm_hist(FILE *       const ifP,
             unsigned int j;
             bool plotted;
             plotted = FALSE;
-            for (j = hist_height - (int)(vscale * hist[WANT_BLU][i]); 
-                 j < hist_height && !plotted; 
+            for (j = histHeight - (int)(vscale * hist[WANT_BLU][i]); 
+                 j < histHeight && !plotted; 
                  ++j) {
                 PPM_PUTB(pixels[j][i], maxval);
                 plotted = dots;
             }
         }
     }
-    ppm_writeppm(stdout, pixels, hist_width, hist_height, maxval, 0);
+    ppm_writeppm(stdout, pixels, histWidth, histHeight, maxval, 0);
+}
+
+
+
+static void
+reportScale(unsigned int const histWidth,
+            unsigned int const range,
+            bool         const verbose) {
+
+    double const hscale = (float)(histWidth-1) / (range-1);
+
+    if (hscale - 1.0 < epsilon && verbose)
+        pm_message("Horizontal scale factor: %g", hscale);
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
     int cols, rows;
     xelval maxval;
     int format;
-    unsigned int hist_width;
+    unsigned int histWidth;
     unsigned int range;
-    double hscale;
-    int hmax;
+    unsigned int hmax;
+    xelval startval, endval;
 
-    pnm_init (&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -451,34 +504,33 @@ main(int argc, char ** argv) {
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
 
-    range = MIN(maxval, cmdline.rval) - cmdline.lval + 1;
+    startval = cmdline.lval;
+    endval   = MIN(maxval, cmdline.rval) + 1;
+
+    range = endval - startval;
 
     if (cmdline.widthSpec)
-        hist_width = cmdline.width;
+        histWidth = cmdline.width;
     else
-        hist_width = range;
-
-    hscale = (float)(hist_width-1) / (range-1);
-    if (hscale - 1.0 < epsilon && cmdline.verbose)
-        pm_message("Horizontal scale factor: %g (maxval = %u)", 
-                   hscale, maxval);
+        histWidth = range;
 
+    reportScale(histWidth, range, cmdline.verbose);
     if (cmdline.nmaxSpec)
-        hmax = cols * rows / hist_width * cmdline.nmax;
+        hmax = cols * rows / histWidth * cmdline.nmax;
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        ppm_hist(ifP, cols, rows, maxval, format,
-                 cmdline.dots, cmdline.white, cmdline.black,
-                 cmdline.colorWanted,
-                 cmdline.verbose, cmdline.lval, cmdline.rval, 
-                 hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale);
+        ppmHist(ifP, cols, rows, maxval, format,
+                cmdline.dots, cmdline.white, cmdline.black,
+                cmdline.colorWanted,
+                cmdline.verbose, startval, endval,
+                histWidth, cmdline.height, cmdline.nmaxSpec, hmax);
         break;
     case PGM_TYPE:
-        pgm_hist(ifP, cols, rows, maxval, format,
-                 cmdline.dots, cmdline.white, cmdline.black,
-                 cmdline.verbose, cmdline.lval, cmdline.rval,
-                 hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale);
+        pgmHist(ifP, cols, rows, maxval, format,
+                cmdline.dots, cmdline.white, cmdline.black,
+                cmdline.verbose, startval, endval,
+                histWidth, cmdline.height, cmdline.nmaxSpec, hmax);
         break;
     case PBM_TYPE:
         pm_error("Cannot do a histogram of a a PBM file");
diff --git a/converter/other/Makefile b/converter/other/Makefile
index 83676aaa..1417cd3a 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -77,9 +77,9 @@ ifeq ($(TIFFLIB_NEEDS_Z),Y)
   endif
 endif
 
-PORTBINARIES =  bmptopnm fitstopnm \
+PORTBINARIES =  avstopam bmptopnm fitstopnm \
 		gemtopnm giftopnm hdifftopam infotopam \
-		pamtodjvurle pamtofits pamtogif \
+		pamtoavs pamtodjvurle pamtofits pamtogif \
 		pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \
 		pamtopam pamtopfm pamtopnm pamtouil \
 		pamtoxvmini \
diff --git a/converter/other/avstopam.c b/converter/other/avstopam.c
new file mode 100644
index 00000000..8d32008c
--- /dev/null
+++ b/converter/other/avstopam.c
@@ -0,0 +1,103 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert an AVS X image to a PAM image
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+
+#include "pm.h"
+#include "pam.h"
+
+
+
+static void
+producePam(FILE *       const avsFileP,
+           struct pam * const pamP) {
+
+    tuple *      tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(pamP);
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            tuple const thisTuple = tuplerow[col];
+            char c;
+            pm_readchar(avsFileP, &c); thisTuple[3] = c;
+            pm_readchar(avsFileP, &c); thisTuple[0] = c;
+            pm_readchar(avsFileP, &c); thisTuple[1] = c;
+            pm_readchar(avsFileP, &c); thisTuple[2] = c;
+        }
+        pnm_writepamrow(pamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    const char * comment = "Produced by avstopam";  /* constant */
+
+    struct pam   outPam;
+    const char * inputFilename;
+    FILE       * inFileP;
+    long         width;
+    long         height;
+
+    pm_proginit(&argc, argv);
+
+    inputFilename = (argc > 1) ? argv[1] : "-";
+
+    inFileP = pm_openr(inputFilename);
+
+    pm_readbiglong(inFileP, &width);
+    pm_readbiglong(inFileP, &height);
+
+    outPam.size             = sizeof(struct pam);
+    outPam.len              = PAM_STRUCT_SIZE(comment_p);
+    outPam.file             = stdout;
+    outPam.format           = PAM_FORMAT;
+    outPam.plainformat      = 0;
+    outPam.width            = width;
+    outPam.height           = height;
+    outPam.depth            = 4;
+    outPam.maxval           = 255;
+    outPam.bytes_per_sample = 1;
+    sprintf(outPam.tuple_type, "RGB_ALPHA");
+    outPam.allocation_depth = 4;
+    outPam.comment_p        = &comment;
+
+    /* Produce a PAM output header.  Note that AVS files *always*
+       contain four channels with one byte per channel.
+    */
+    pnm_writepaminit(&outPam);
+
+    producePam(inFileP, &outPam);
+
+    pm_closer(inFileP);
+
+    return 0;
+}
diff --git a/converter/other/pamtoavs.c b/converter/other/pamtoavs.c
new file mode 100644
index 00000000..4764c9e8
--- /dev/null
+++ b/converter/other/pamtoavs.c
@@ -0,0 +1,150 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert a PAM image to an AVS X image
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include "pm.h"
+#include "pam.h"
+
+
+
+static char
+sample2char(sample const s,
+            sample const maxval) {
+/* Scale down a sample to a single byte. */
+
+    return maxval==255 ? s : s * 255 / maxval;
+}
+
+
+#define THIS_SAMPLE_CHAR(PLANE) \
+  sample2char(tuplerow[col][PLANE], pamP->maxval)
+
+static void
+produceAvs(struct pam * const pamP,
+           FILE *       const avsFileP) {
+
+    tuple * tuplerow;
+
+    /* Write the AVS header (image width and height as 4-byte
+       big-endian integers).
+    */
+    pm_writebiglong(avsFileP, pamP->width);
+    pm_writebiglong(avsFileP, pamP->height);
+
+    /* Write the AVS data (alpha, red, green, blue -- one byte apiece. */
+    tuplerow = pnm_allocpamrow(pamP);
+    switch (pamP->depth) {
+    case 1: {
+        /* Black-and-white or grayscale, no alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, (char)255);
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+            }
+        }
+    } break;
+
+    case 2: {
+        /* Black-and-white or grayscale plus alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+            }
+        }
+    } break;
+
+    case 3: {
+        /* RGB, no alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, (char)255);
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(2));
+            }
+        }
+    } break;
+
+    case 4: {
+        /* RGB plus alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow( pamP, tuplerow );
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(3));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(2));
+            }
+        }
+    } break;
+
+    default:
+        pm_error("Unrecognized PAM depth %u.  We understand only "
+                 "1, 2, 3, and 4", pamP->depth);
+        break;
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    struct pam   inPam;
+    const char * inputFilename;
+    FILE       * inFileP;
+
+    pm_proginit(&argc, argv);
+
+    inputFilename = (argc > 1) ? argv[1] : "-";
+
+    inFileP = pm_openr(inputFilename);
+
+    pnm_readpaminit(inFileP, &inPam, PAM_STRUCT_SIZE(tuple_type));
+
+    produceAvs(&inPam, stdout);
+
+    pm_closer(inFileP);
+
+    return 0;
+}
+
diff --git a/converter/other/pamtosvg/thin-image.c b/converter/other/pamtosvg/thin-image.c
index 40ced794..86d1037c 100644
--- a/converter/other/pamtosvg/thin-image.c
+++ b/converter/other/pamtosvg/thin-image.c
@@ -171,7 +171,7 @@ thin_image(bitmap_type *image, bool bgSpec, pixel bg,
 	    if (PPM_ISGRAY(background))
             bg_color = PPM_GETR(background);
 	    else
-            bg_color = PPM_LUMIN(background);
+            bg_color = ppm_luminosity(background);
 
 	    for (n = num_pixels - 1; n >= 0L; --n)
 	    {
@@ -306,7 +306,7 @@ void thin1(bitmap_type *image, unsigned char colour)
       if (PPM_ISGRAY(background))
           bg_color = PPM_GETR(background);
       else
-          bg_color = PPM_LUMIN(background);
+          bg_color = ppm_luminosity(background);
 
       LOG (" Thinning image.....\n "); 
       xsize = image->width;
diff --git a/converter/other/pnmtosgi.c b/converter/other/pnmtosgi.c
index 472b5197..169125b3 100644
--- a/converter/other/pnmtosgi.c
+++ b/converter/other/pnmtosgi.c
@@ -13,11 +13,16 @@
 ** implied warranty.
 **
 ** 29Jan94: first version
+
+** Feb 2010 afu
+** Added dimension check to prevent short int from overflowing
 */
 #include "pnm.h"
 #include "sgi.h"
 #include "mallocvar.h"
 
+
+
 /*#define DEBUG*/
 
 typedef short       ScanElem;
@@ -42,6 +47,7 @@ static int rle_compress ARGS((ScanElem *inbuf, int cols));
 
 #define MAXVAL_BYTE     255
 #define MAXVAL_WORD     65535
+#define INT16MAX        32767
 
 static char storage = STORAGE_RLE;
 static ScanLine * channel[3];
@@ -87,9 +93,7 @@ write_header(int const cols,
 
 
 int
-main(argc, argv)
-    int argc;
-    char *argv[];
+main(int argc,char * argv[])
 {
     FILE *ifp;
     int argn;
@@ -131,6 +135,9 @@ main(argc, argv)
         pm_usage(usage);
 
     pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
+    if( rows>INT16MAX || cols>INT16MAX )
+      pm_error ("Input image is too large.");
+
     pnmrow = pnm_allocrow(cols);
 
     switch( PNM_FORMAT_TYPE(format) ) {
@@ -172,9 +179,7 @@ main(argc, argv)
 
 
 static void
-write_table(table, tabsize)
-    long *table;
-    int tabsize;
+write_table(long * table, int const tabsize)
 {
     int i;
     long offset;
@@ -194,9 +199,8 @@ write_table(table, tabsize)
 
 
 static void
-write_channels(cols, rows, channels, put)
-    int cols, rows, channels;
-    void (*put) ARGS((short));
+write_channels(int const cols,int const rows, int const channels,
+               void (*put) (short))
 {
     int i, row, col;
 
@@ -214,7 +218,7 @@ write_channels(cols, rows, channels, put)
 }
 
 static void
-put_big_short(short s)
+put_big_short(short const s)
 {
     if ( pm_writebigshort( stdout, s ) == -1 )
         pm_error( "write error" );
@@ -222,8 +226,7 @@ put_big_short(short s)
 
 
 static void
-put_big_long(l)
-    long l;
+put_big_long(long const l)
 {
     if ( pm_writebiglong( stdout, l ) == -1 )
         pm_error( "write error" );
@@ -231,15 +234,16 @@ put_big_long(l)
 
 
 static void
-put_short_as_byte(short s)
+put_short_as_byte(short const s)
 {
     put_byte((unsigned char)s);
 }
 
 
 static long *
-build_channels(FILE *ifp, int cols, int rows, xelval maxval, 
-               int format, int bpc, int channels)
+build_channels(FILE * const ifp, int const cols, int const rows,
+               xelval const maxval, int const format,
+               int const bpc, int const channels)
 {
     int i, row, col, sgirow;
     long *table = NULL;
@@ -286,11 +290,10 @@ build_channels(FILE *ifp, int cols, int rows, xelval maxval,
 
 
 static ScanElem *
-compress(temp, row, rows, cols, chan_no, table, bpc)
-    ScanElem *temp;
-    int row, rows, cols, chan_no;
-    long *table;
-    int bpc;
+compress(ScanElem * temp,
+         int const row,  int const rows,
+         int const cols, int const chan_no,
+         long * table, int const bpc)
 {
     int len, i, tabrow;
     ScanElem *p;
@@ -323,9 +326,7 @@ slightly modified RLE algorithm from ppmtoilbm.c
 written by Robert A. Knop (rknop@mop.caltech.edu)
 */
 static int
-rle_compress(inbuf, size)
-    ScanElem *inbuf;
-    int size;
+rle_compress(ScanElem * const inbuf, int const size)
 {
     int in, out, hold, count;
     ScanElem *outbuf = rletemp;
diff --git a/converter/other/ppmtopgm.c b/converter/other/ppmtopgm.c
index 86e7ae6a..e20c5660 100644
--- a/converter/other/ppmtopgm.c
+++ b/converter/other/ppmtopgm.c
@@ -36,9 +36,9 @@ convertRaster(FILE *       const ifP,
                 outputRow[col] = (gray) ppm_fastlumin(inputRow[col]);
         } else {
             /* Can't use fast approximation, so fall back on floats. */
-            int col;
+            unsigned int col;
             for (col = 0; col < cols; ++col) 
-                outputRow[col] = (gray) (PPM_LUMIN(inputRow[col]) + 0.5);
+                outputRow[col] = ppm_luminosity(inputRow[col]);
         }
         pgm_writepgmrow(ofP, outputRow, cols, maxval, 0);
     }
diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c
index cefbdc95..9eab0416 100644
--- a/converter/pbm/pbmtogem.c
+++ b/converter/pbm/pbmtogem.c
@@ -27,17 +27,21 @@
 *  removed rounding of the imagewidth to the next word boundary
 *  removed arbitrary limit to imagewidth
 *  changed pattern length to 1 to simplify locating of compressable parts
-*	in real world images
+*       in real world images
 *  add solid run and pattern run compression
 *
 *  Deficiencies:
 *  Compression of repeated scanlines not added
 *  
-*	Johann Haider (jh@fortec.tuwien.ac.at)
+*       Johann Haider (jh@fortec.tuwien.ac.at)
 *
 * 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
 * Changed to remove architecture dependencies
 * Added compression of repeated scanlines
+*
+* Feb 2010 afu
+* Added dimension check to prevent short int from overflowing
+* Changed code style (ANSI-style function definitions, etc.)
 */
 
 #include <stdio.h>
@@ -47,59 +51,11 @@
 #define SOLID_0 0
 #define SOLID_1 0xff
 #define MINRUN 4
+#define INT16MAX 32767
+
 #define putsolid(v,c) putc((v&0x80)|c, stdout)
 #define putpattern(v,c) putc(0, stdout);putc(c, stdout);putc(v, stdout)
 
-static void putinit ARGS ((int rows, int cols));
-static void putbit ARGS(( bit b ));
-static void putitem ARGS(( void ));
-static void putrow ARGS(( void ));
-static void flushrow ARGS ((void));
-static void putstring ARGS((register unsigned char *p, register int n));
-
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, row, col;
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
-
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-
-    bitrow = pbm_allocrow( cols );
-
-    putinit (rows, cols);
-    for ( row = 0; row < rows; ++row )
-	{
-#ifdef DEBUG
-	fprintf (stderr, "row %d\n", row);
-#endif
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    putbit( *bP );
-        putrow( );
-        }
-    flushrow ();
-
-    pm_close( ifp );
-
-
-    exit( 0 );
-    }
-
 static short item;
 static int outcol, outmax;
 static short bitsperitem, bitshift;
@@ -107,9 +63,9 @@ static short linerepeat;
 static unsigned char *outrow, *lastrow;
 
 static void
-putinit (rows, cols)
-     int rows, cols;
+putinit (int const rows, int const cols)
 {
+
   if (pm_writebigshort (stdout, (short) 1) == -1 /* Image file version */
       || pm_writebigshort (stdout, (short) 8) == -1 /* Header length */
       || pm_writebigshort (stdout, (short) 1) == -1 /* Number of planes */
@@ -130,17 +86,6 @@ putinit (rows, cols)
 }
 
 static void
-putbit( bit b )
-    {
-    if ( bitsperitem == 8 )
-	putitem( );
-    ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    --bitshift;
-    }
-
-static void
 putitem( )
     {
     outrow[outcol++] = item;
@@ -149,19 +94,93 @@ putitem( )
     bitshift = 7;
     }
 
+
+static void
+putbit( bit const b )
+    {
+    if ( bitsperitem == 8 )
+        putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+        item += 1 << bitshift;
+    --bitshift;
+    }
+
+
 static void
-putstring (p, n)
-register unsigned char *p;
-register int n;
+putstring ( unsigned char *p, int n)
 {
 #ifdef DEBUG
     fprintf (stderr, "Bitstring, length: %d, pos %d\n", n, outcol);
 #endif
     (void) putc((char) 0x80, stdout);     /* a Bit string */
-    (void) putc(n, stdout);	/* count */
+    (void) putc(n, stdout);     /* count */
     fwrite( p, n, 1, stdout );
 }
 
+
+static void
+flushrow( )
+    {
+    unsigned char *outp, *p, *q;
+    int count;
+    int col = outmax;
+
+    if (linerepeat > 1)
+      {
+        /* Put out line repeat count */
+        fwrite ("\0\0\377", 3, 1, stdout);
+        putchar (linerepeat);
+      }
+    for (outp = p = lastrow; col > 0;)
+    {
+            for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
+            if (count > MINRUN)
+            {
+                if (p > outp)
+                {
+                    putstring (outp, p-outp);
+                    outp = p;
+                }
+                col -= count;
+                switch (*p)
+                {
+                case SOLID_0:
+#ifdef DEBUG
+/*                      if (outcol > 0) */
+                        fprintf (stderr, "Solid run 0, length: %d\n", count);
+#endif
+                        putsolid (SOLID_0, count);
+                        break;
+
+                case SOLID_1:
+#ifdef DEBUG
+                        fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
+#endif
+                        putsolid (SOLID_1, count);
+                        break;
+                default:
+#ifdef DEBUG
+                        fprintf (stderr, "Pattern run, length: %d\n", count);
+#endif
+                        putpattern (*p, count);
+                        break;
+                }
+                outp = p = q;
+            }
+            else
+            {
+                p++;
+                col--;
+            }
+    }           
+    if (p > outp)
+         putstring (outp, p-outp);
+    if (ferror (stdout))
+      pm_error ("write error");
+}
+
+
 static void
 putrow( )
 {
@@ -173,7 +192,7 @@ putrow( )
     {
       unsigned char *temp;
       if (linerepeat != -1) /* Unless first line */
-	flushrow ();
+        flushrow ();
       /* Swap the pointers */
       temp = outrow; outrow = lastrow; lastrow = temp;
       linerepeat = 1;
@@ -183,64 +202,47 @@ putrow( )
     linerepeat++;
 }
 
-static void
-flushrow( )
-    {
-    register unsigned char *outp, *p, *q;
-    register int count;
-    int col = outmax;
 
-    if (linerepeat > 1)
-      {
-	/* Put out line repeat count */
-	fwrite ("\0\0\377", 3, 1, stdout);
-	putchar (linerepeat);
-      }
-    for (outp = p = lastrow; col > 0;)
+int
+main( int argc, char* argv[])
     {
-	    for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
-	    if (count > MINRUN)
-	    {
-		if (p > outp)
-		{
-		    putstring (outp, p-outp);
-		    outp = p;
-		}
-		col -= count;
-		switch (*p)
-		{
-		case SOLID_0:
-#ifdef DEBUG
-/*			if (outcol > 0) */
-			fprintf (stderr, "Solid run 0, length: %d\n", count);
-#endif
-			putsolid (SOLID_0, count);
-			break;
+    FILE* ifp;
+    bit* bitrow;
+    int rows, cols, format, row, col;
 
-		case SOLID_1:
-#ifdef DEBUG
-			fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
-#endif
-			putsolid (SOLID_1, count);
-			break;
-		default:
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+
+    if( rows>INT16MAX || cols>INT16MAX )
+      pm_error ("Input image is too large.");
+
+
+    bitrow = pbm_allocrow( cols );
+
+    putinit (rows, cols);
+    for ( row = 0; row < rows; ++row )
+        {
 #ifdef DEBUG
-			fprintf (stderr, "Pattern run, length: %d\n", count);
+        fprintf (stderr, "row %d\n", row);
 #endif
-			putpattern (*p, count);
-			break;
-		}
-		outp = p = q;
-	    }
-	    else
-	    {
-		p++;
-		col--;
-	    }
-    }		
-    if (p > outp)
-         putstring (outp, p-outp);
-    if (ferror (stdout))
-      pm_error ("write error");
-}
+        pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0; col < cols; ++col )
+            putbit( bitrow[col] );
+        putrow( );
+        }
+    flushrow ();
+
+    pm_close( ifp );
 
+
+    exit( 0 );
+    }
diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c
index 508e8e92..3fdd805d 100644
--- a/converter/pbm/pbmtoybm.c
+++ b/converter/pbm/pbmtoybm.c
@@ -9,100 +9,129 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+**
+** Feb 2010 afu
+** Added dimension check to prevent short int from overflowing
+** Changed code style (ANSI-style function definitions, etc.)
 */
 
 #include <stdio.h>
+
+#include "pm.h"
 #include "pbm.h"
 
 #define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
+#define INT16MAX 32767
 
-static void putinit ARGS(( int cols, int rows ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
+static long item;
+static int bitsperitem, bitshift;
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, padright, row, col;
-
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
-    
-    /* Compute padding to round cols up to the nearest multiple of 16. */
-    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
 
-    putinit( cols, rows );
-    for ( row = 0; row < rows; ++row )
-	{
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    putbit( *bP );
-	for ( col = 0; col < padright; ++col )
-	    putbit( 0 );
-        }
+static void
+putitem(void) {
 
-    if ( ifp != stdin )
-	fclose( ifp );
+    pm_writebigshort(stdout, item);
 
-    putrest( );
+    item        = 0;
+    bitsperitem = 0;
+    bitshift    = 0;
+}
 
-    exit( 0 );
-    }
 
-static long item;
-static int bitsperitem, bitshift;
 
 static void
-putinit( cols, rows )
-    int cols, rows;
-    {
-    pm_writebigshort( stdout, YBM_MAGIC );
-    pm_writebigshort( stdout, cols );
-    pm_writebigshort( stdout, rows );
-    item = 0;
+putinit(int const cols,
+        int const rows) {
+
+    pm_writebigshort(stdout, YBM_MAGIC);
+    pm_writebigshort(stdout, cols);
+    pm_writebigshort(stdout, rows);
+
+    item        = 0;
     bitsperitem = 0;
-    bitshift = 0;
-    }
+    bitshift    = 0;
+}
+
+
 
 static void
-putbit( bit b )
-    {
-    if ( bitsperitem == 16 )
-	putitem( );
+putbit(bit const b) {
+
+    if (bitsperitem == 16)
+        putitem();
+
     ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
+
+    if (b == PBM_BLACK)
+        item += 1 << bitshift;
+
     ++bitshift;
-    }
+}
+
+
 
 static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
+putrest(void) {
+
+    if (bitsperitem > 0)
+        putitem();
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    FILE * ifP;
+    bit * bitrow;
+    int rows;
+    int cols;
+    int format;
+    unsigned int padright;
+    unsigned int row;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
     }
 
-static void
-putitem( )
-    {
-    pm_writebigshort( stdout, item );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 0;
+    ifP = pm_openr(inputFile);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    if (rows > INT16MAX || cols > INT16MAX)
+        pm_error("Input image is too large.");
+
+    bitrow = pbm_allocrow(cols);
+    
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ((cols + 15) / 16) * 16 - cols;
+
+    putinit(cols, rows);
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+
+        for (col = 0; col < cols; ++col)
+            putbit(bitrow[col]);
+
+        for (col = 0; col < padright; ++col)
+            putbit(0);
     }
+
+    if (ifP != stdin)
+        fclose(ifP);
+
+    putrest();
+
+    return 0;
+}
diff --git a/converter/pbm/ybmtopbm.c b/converter/pbm/ybmtopbm.c
index 739e168a..3d012483 100644
--- a/converter/pbm/ybmtopbm.c
+++ b/converter/pbm/ybmtopbm.c
@@ -10,104 +10,122 @@
 ** implied warranty.
 */
 
-#include <stdio.h>
+#include "pm.h"
 #include "pbm.h"
 
-static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
-static bit getbit ARGS(( FILE* file ));
+static short const ybmMagic = ( ( '!' << 8 ) | '!' );
 
-#define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    short rows, cols, padright, row, col;
-    short depth;
-
-    pbm_init( &argc, argv );
 
-    if ( argc > 2 )
-	pm_usage( "[ybmfile]" );
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
+static int item;
+static int bitsInBuffer, bitshift;
 
-    getinit( ifp, &cols, &rows, &depth, &padright );
-    if ( depth != 1 )
-	pm_error(
-	    "YBM file has depth of %d, must be 1",
-	    (int) depth );
 
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
 
-    for ( row = 0; row < rows; ++row )
-	{
-	/* Get data. */
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    *bP = getbit( ifp );
-	/* Discard line padding */
-        for ( col = 0; col < padright; ++col )
-	    (void) getbit( ifp );
-	pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
+static void
+getinit(FILE *  const ifP,
+        short * const colsP,
+        short * const rowsP,
+        short * const depthP,
+        short * const padrightP) {
 
-    pm_close( ifp );
-    pm_close( stdout );
+    short magic;
+    int rc;
 
-    exit( 0 );
-    }
+    rc = pm_readbigshort(ifP, &magic);
+    if (rc == -1)
+        pm_error("EOF / read error");
 
-static int item;
-static int bitsperitem, bitshift;
+    if (magic != ybmMagic)
+        pm_error("bad magic number in YBM file");
 
-static void
-getinit( file, colsP, rowsP, depthP, padrightP )
-    FILE* file;
-    short* colsP;
-    short* rowsP;
-    short* depthP;
-    short* padrightP;
-    {
-    short magic;
+    rc = pm_readbigshort(ifP, colsP);
+    if (rc == -1 )
+        pm_error("EOF / read error");
 
-    if ( pm_readbigshort( file, &magic ) == -1 )
-	pm_error( "EOF / read error" );
-    if ( magic != YBM_MAGIC )
-	pm_error( "bad magic number in YBM file" );
-    if ( pm_readbigshort( file, colsP ) == -1 )
-	pm_error( "EOF / read error" );
-      if ( pm_readbigshort( file, rowsP ) == -1 )
-	pm_error( "EOF / read error" );
+    rc = pm_readbigshort(ifP, rowsP);
+    if (rc == -1)
+        pm_error("EOF / read error");
 
     *depthP = 1;
-    *padrightP = ( ( *colsP + 15 ) / 16 ) * 16 - *colsP;
-    bitsperitem = 0;
-    }
+    *padrightP = ((*colsP + 15) / 16) * 16 - *colsP;
+}
+
+
 
 static bit
-getbit( file )
-    FILE* file;
-    {
+getbit(FILE * const ifP) {
+
     bit b;
 
-    if ( bitsperitem == 0 )
-	{
-	item = getc(file) | getc(file)<<8;
-	if ( item == EOF )
-	    pm_error( "EOF / read error" );
-	bitsperitem = 16;
-	bitshift = 0;
-	}
-    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
-    --bitsperitem;
+    if (bitsInBuffer == 0) {
+        item = (getc(ifP) << 8) | (getc(ifP) << 0);
+
+        if (item == EOF)
+            pm_error("EOF / read error");
+
+        bitsInBuffer = 16;
+        bitshift = 0;
+    }
+
+    b = ((item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
+    --bitsInBuffer;
     ++bitshift;
     return b;
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    bit * bitrow;
+    short rows, cols, padright;
+    unsigned int row;
+    short depth;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
+    }
+
+    ifP = pm_openr(inputFile);
+
+    bitsInBuffer = 0;
+
+    getinit(ifP, &cols, &rows, &depth, &padright);
+    if (depth != 1)
+        pm_error("YBM file has depth of %u, must be 1", (unsigned)depth);
+    
+    pbm_writepbminit(stdout, cols, rows, 0);
+
+    bitrow = pbm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        /* Get raster. */
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col)
+            bitrow[col] = getbit(ifP);
+
+        /* Discard line padding */
+        for (col = 0; col < padright; ++col)
+            getbit(ifP);
+        pbm_writepbmrow(stdout, bitrow, cols, 0);
     }
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/pgm/pgmtolispm.c b/converter/pgm/pgmtolispm.c
index abb85494..7d931fb3 100644
--- a/converter/pgm/pgmtolispm.c
+++ b/converter/pgm/pgmtolispm.c
@@ -14,129 +14,167 @@
 **   usually a color image; but a color map is not written in the file, so we
 **   treat this as a graymap instead.  To convert a color image to Lispm 
 **   format, you must convert it to a pgm, and hand-edit a color map...  Ick.
+**
+** Feb 2010 afu
+** Added dimension check to prevent short int from overflowing
+** Changed code style (ANSI-style function definitions, etc.)
 */
 
-#include <stdio.h>
+#include "pm.h"
 #include "pgm.h"
 
 #define LISPM_MAGIC  "This is a BitMap file"
+#define INT16MAX 32767
 
-static void putinit ARGS(( int cols, int rows, int depth ));
-static int depth_to_word_size ARGS(( int depth ));
-static void putval ARGS(( gray b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    gray *grayrow;
-    register gray* gP;
-    int rows, cols, depth, format, padright, row, col;
-    gray maxval;
+static unsigned int item;
+static unsigned int bitsperitem, maxbitsperitem, bitshift;
 
+static unsigned int
+depth_to_word_size(unsigned int const depth) {
 
-    pgm_init( &argc, argv );
+    /* Lispm architecture specific - if a bitmap is written    */
+    /* out with a depth of 5, it really has a depth of 8, and  */
+    /* is stored that way in the file.                         */
 
-    if ( argc > 2 )
-	pm_usage( "[pgmfile]" );
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
+    unsigned int const wordSize = 
+        depth ==  1 ?  1 :
+        depth ==  2 ?  2 :
+        depth <=  4 ?  4 :
+        depth <=  8 ?  8 :
+        depth <= 16 ? 16 :
+        depth <= 32 ? 32 :
+        0;
 
-    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
-    grayrow = pgm_allocrow( cols );
-    depth = pm_maxvaltobits( maxval );
+    if (wordSize == 0)
+        pm_error("depth was %u, which is not in the range 1-32", depth);
 
-    /* Compute padding to round cols up to the nearest multiple of 32. */
-    padright = ( ( cols + 31 ) / 32 ) * 32 - cols;
+    return wordSize;
+}
 
-    putinit( cols, rows, depth );
-    for ( row = 0; row < rows; ++row )
-	{
-	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
-        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
-	    putval( *gP );
-	for ( col = 0; col < padright; ++col )
-	    putval( 0 );
-        }
 
-    pm_close( ifp );
 
-    putrest( );
+static void
+putinit(unsigned int const cols,
+        unsigned int const rows,
+        unsigned int const depth) {
 
-    exit( 0 );
-    }
+    unsigned int const cols32 = ((cols + 31 ) / 32) * 32;
 
-static unsigned int item;
-static unsigned int bitsperitem, maxbitsperitem, bitshift;
+    unsigned int i;
 
-static void
-putinit( cols, rows, depth )
-    int cols, rows, depth;
-    {
-    int i;
-    int cols32 = ( ( cols + 31 ) / 32 ) * 32;	/* Lispms are able to write bit files that are not mod32 wide, but we   */
-						/* don't.  This should be ok, since bit arrays which are not mod32 wide */
-    printf(LISPM_MAGIC);			/* are pretty useless on a lispm (can't hand them to bitblt).		*/
-    pm_writelittleshort( stdout, cols );
-    pm_writelittleshort( stdout, rows );
-    pm_writelittleshort( stdout, cols32 );
+    /* Lispms are able to write bit files that are not mod32 wide, but we   */
+    /* don't.  This should be ok, since bit arrays which are not mod32 wide */
+    /* are pretty useless on a lispm (can't hand them to bitblt).           */
+
+    if (rows > INT16MAX || cols > INT16MAX || cols32 > INT16MAX)
+        pm_error("Input image is too large.");
+
+    printf(LISPM_MAGIC);
+
+    pm_writelittleshort(stdout, cols);
+    pm_writelittleshort(stdout, rows);
+    pm_writelittleshort(stdout, cols32);
     putchar(depth & 0xFF);
 
-    for ( i = 0; i < 9; ++i )
-	putchar( 0 );	/* pad bytes */
+    for (i = 0; i < 9; ++i)
+        putchar(0);   /* pad bytes */
 
-    item = 0;
-    bitsperitem = 0;
-    maxbitsperitem = depth_to_word_size( depth );
-    bitshift = 0;
-    }
+    item           = 0;
+    bitsperitem    = 0;
+    maxbitsperitem = depth_to_word_size(depth);
+    bitshift       = 0;
+}
 
-static int
-depth_to_word_size (depth)	/* Lispm architecture specific - if a bitmap is written    */
-  int depth;			/* out with a depth of 5, it really has a depth of 8, and  */
-{				/* is stored that way in the file.			   */
-    if (depth==0 || depth==1)	return ( 1);
-    else if (depth ==  2)	return ( 2);
-    else if (depth <=  4)	return ( 4);
-    else if (depth <=  8)	return ( 8);
-    else if (depth <= 16)	return (16);
-    else if (depth <= 32)	return (32);
-    else {
-      pm_error( "depth was %d, which is not in the range 1-32", depth );
-      return(-1);  /* Should never reach here */
-  }
+
+
+static void
+putitem(void) {
+
+    pm_writelittlelong(stdout, ~item);
+
+    item        = 0;
+    bitsperitem = 0;
+    bitshift    = 0;
 }
 
 
 
 static void
-putval( gray b )
-    {
-    if ( bitsperitem == 32 )
-	putitem( );
-    item = item | ( b << bitshift );
+putval(gray const b) {
+
+    if (bitsperitem == 32)
+        putitem();
+
+    item        = item | (b << bitshift);
     bitsperitem = bitsperitem + maxbitsperitem;
-    bitshift = bitshift + maxbitsperitem;
-    }
+    bitshift    = bitshift + maxbitsperitem;
+}
+
+
 
 static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
+putrest(void) {
+
+    if (bitsperitem > 0)
+        putitem();
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    gray * grayrow;
+    int rows;
+    int cols;
+    unsigned int depth;
+    int format;
+    unsigned int padright;
+    unsigned int row;
+    gray maxval;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
     }
 
-static void
-putitem( )
-    {
-    pm_writelittlelong( stdout, ~item );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 0;
+    ifP = pm_openr(inputFile);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+
+    grayrow = pgm_allocrow(cols);
+    depth = pm_maxvaltobits(maxval);
+
+    /* Compute padding to round cols up to the nearest multiple of 32. */
+    padright = ((cols + 31) / 32) * 32 - cols;
+
+    putinit(cols, rows, depth);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col)
+            putval(grayrow[col]);
+
+        for (col = 0; col < padright; ++col)
+            putval(0);
     }
+
+    pm_close(ifP);
+
+    putrest();
+
+    return 0;
+}
diff --git a/converter/ppm/Makefile b/converter/ppm/Makefile
index adc3a400..1bade11a 100644
--- a/converter/ppm/Makefile
+++ b/converter/ppm/Makefile
@@ -20,7 +20,7 @@ PORTBINARIES =	411toppm eyuvtoppm gouldtoppm ilbmtoppm imgtoppm \
 		ppmtowinicon ppmtoxpm ppmtoyuv ppmtoyuvsplit \
 		qrttoppm rawtoppm rgb3toppm spctoppm \
 		sputoppm tgatoppm winicontoppm ximtoppm xpmtoppm xvminitoppm \
-		yuvtoppm yuvsplittoppm
+		yuvsplittoppm yuvtoppm 
 
 MATHBINARIES = sldtoppm 
 
diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c
index a703cf39..2567666d 100644
--- a/converter/ppm/ppmtompeg/jpeg.c
+++ b/converter/ppm/ppmtompeg/jpeg.c
@@ -360,7 +360,7 @@ JMovie2JPEG(const char * const infilename,
  *
  *      allocate and initialize JPEG decompression object
  *      specify data source (eg, a file)
- *      jpeg_read_header();     // obtain image dimensions and other parameters
+ *      jpeg_read_header();      obtain image dimensions and other parameters
  *      set parameters for decompression
  *      jpeg_start_decompress();
  *      while (scan lines remain to be read)
diff --git a/doc/HISTORY b/doc/HISTORY
index be62355b..d5097040 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,40 +4,69 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-10.03.23 BJH  Release 10.49.05
+10.03.27 BJH  Release 10.50.00
 
-              pbmtext: fix crash when BDF font file contains spurious
-              blank line.  Ignore such blank lines.
-              
-10.03.17 BJH  Release 10.49.04
+              Add pamtoavs, avstopam.  Thanks Scott Pakin.
 
-              ppmtoilbm: fix arithmetic overflow with image dimension
-              represented as 16 bit integer.
+              Add pampaintspill.
 
-              pbmpage: fix garbage output.
+              pnmconvol: Add -normalize .
 
-              Build: don't fail due to SIGPWR being undefined.
+              pm_system(): Close extraneous file descriptors that, among
+              other things, prevent child from seeing EOF.
 
-10.02.23 BJH  Release 10.49.03
+              libnetpbm: Add PNM_GETR(), PNM_GETG(), PNM_GETB().  Same
+              as PPM_GETR(), etc.
 
-              pnmhistmap: Fix crash with -width.  Always broken.
+              libnetpbm: Add ppm_luminosity().  Same as PPM_LUMIN, but
+              returns pixval.
 
-              pm_system(): Close extraneous file descriptors that, among
-              other things, prevent child from seeing EOF.
+              pnmhisteq: Equalize based on luminosity alone, rather than a
+              strange combination of luminosity and HSV value.
 
-10.01.25 BJH  Release 10.49.02
+              pamenlarge: Make special fast path for scale factors up to 10
+              (2, 3, and 5 already existed).  Thanks Prophet of the Way
+              <afu@wta.att.ne.jp>.
 
-              pamtosvg: fix bug: occasional crash with out of range error.
+              pamflip: Speed up for most images.  Thanks Prophet of the Way
+              <afu@wta.att.ne.jp>.
+
+              ybmtopbm: Assume YBM format has raster in natural order
+              instead of byte-reversed.  This is what pbmtoybm creates,
+              and is most logical.  We don't know if there are any existing
+              YBM images or generators of them other than pbmtoybm.
 
-10.01.11 BJH  Release 10.49.01
+              pbmpscale: speedup.
+
+              pbmclean: speedup.
+
+              pbmtext: fix crash when BDF font file contains spurious
+              blank line.  Ignore such blank lines.
+              
+              pbmpscale: fix arithmetic overflow on output image dimensions.
+
+              pbmtogem, pbmtoybm, pgmtolispm, ppmtoilbm, pnmtosgi: fix
+              arithmetic overflow with image dimension represented as
+              16 bit integer.
+
+              pbmpage: fix garbage output.
+
+              pnmhistmap: Fix crash with -width.  Always broken.
 
               libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp();
-              broken in 10.47.00.
+              broken in 10.47.
 
               palmtopnm: fix incorrect "PALM_DIRECT_COLOR_FLAG is not valid
-              for version 3 encoding type" failure.
+              for version 3 encoding type" failure.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
+
+              pamtosvg: fix bug: occasional crash with out of range error.
+              Introduced in 10.42.
 
               palmtopnm: fix incorrect output with version 3 direct color.
+              Thanks Paul Bolle <pebolle@tiscali.nl>.
+
+              Build: don't fail due to SIGPWR being undefined.
 
 09.12.30 BJH  Release 10.49.00
 
diff --git a/doc/INSTALL b/doc/INSTALL
index 92e78540..9412b296 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -259,8 +259,11 @@ and which one is default is controlled by the DEFAULT_TARGET make
 variable in config.mk, and its value is one of the choices you
 make via the Configure dialog.
 
-The standard build is the conventional one.  The merge build is a way
-to reduce disk space and other resource usage in some configurations.
+The standard build is the conventional one.  The merge build is a way to
+reduce disk space and other resource usage in some configurations.  These are
+rare configurations, mostly antique ones.  The advent of shared libraries
+largely obsoleted the merge build.  In some configurations, Netpbm uses _more_
+resources with the merge build.
 
 In the standard build, hundreds of separate programs get built: ppmtogif,
 pamcomp, etc.
diff --git a/editor/Makefile b/editor/Makefile
index bc0f5913..29cdcab1 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -20,7 +20,7 @@ PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
 	       pamdice pamditherbw pamedge \
 	       pamenlarge \
 	       pamflip pamfunc pammasksharpen \
-	       pamperspective \
+	       pampaintspill pamperspective \
 	       pamscale pamsistoaglyph pamstretch pamthreshold pamundice \
 	       pbmclean pbmmask pbmpscale pbmreduce \
 	       pgmdeshadow pgmenhance \
diff --git a/editor/pamcut.c b/editor/pamcut.c
index f659ee70..5fdc6788 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -28,7 +28,7 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespec of input file */
+    const char *inputFileName;  /* File name of input file */
 
     /* The following describe the rectangle the user wants to cut out. 
        the value UNSPEC for any of them indicates that value was not
@@ -51,7 +51,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+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
@@ -87,7 +87,7 @@ parseCommandLine(int argc, char ** const argv,
     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);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->width < 0)
@@ -103,10 +103,10 @@ parseCommandLine(int argc, char ** const argv,
 
     switch (argc-1) {
     case 0:
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
         break;
     case 1:
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
         break;
     case 4:
     case 5: {
@@ -137,9 +137,9 @@ parseCommandLine(int argc, char ** const argv,
         }
 
         if (argc-1 == 4)
-            cmdlineP->inputFilespec = "-";
+            cmdlineP->inputFileName = "-";
         else
-            cmdlineP->inputFilespec = argv[5];
+            cmdlineP->inputFileName = argv[5];
         break;
     }
     }
@@ -669,7 +669,7 @@ cutOneImage(FILE *             const ifP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     FILE * const ofP = stdout;
 
@@ -677,11 +677,11 @@ main(int argc, char *argv[]) {
     FILE * ifP;
     bool eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     eof = FALSE;
     while (!eof) {
diff --git a/editor/pamenlarge.c b/editor/pamenlarge.c
index b3039424..dd61f2cf 100644
--- a/editor/pamenlarge.c
+++ b/editor/pamenlarge.c
@@ -115,6 +115,8 @@ enlargePbmRowHorizontally(struct pam *          const inpamP,
     static unsigned char const trp3[8] = { 
         0x00, 0x07, 0x38, 0x3F, 0xC0, 0xC7, 0xF8, 0xFF };
 
+    static unsigned char const quad[4] = { 0x00, 0x0F, 0xF0, 0xFF };
+
     static unsigned char const quin2[8] = {
         0x00, 0x01, 0x3E, 0x3F, 0xC0, 0xC1, 0xFE, 0xFF };
 
@@ -129,33 +131,127 @@ enlargePbmRowHorizontally(struct pam *          const inpamP,
     case 1:  break; /* outrow set to inrow */
     case 2:  /* Make outrow using prefabricated parts (same for 3, 5). */ 
         for (colChar = 0; colChar < inColChars; ++colChar) { 
-            outrow[colChar*2]  = dbl[(inrow[colChar] & 0xF0) >> 4];
-            outrow[colChar*2+1]= dbl[inrow[colChar] & 0x0F];
+            outrow[colChar*2]   = dbl[(inrow[colChar] & 0xF0) >> 4];
+            outrow[colChar*2+1] = dbl[(inrow[colChar] & 0x0F) >> 0];
             /* Possible outrow overrun by one byte. */
         }
-        break;   
+        break;
+
     case 3:
         for (colChar = 0; colChar < inColChars; ++colChar) { 
-            outrow[colChar*3]  = trp1[(inrow[colChar] & 0xF0) >> 5];
-            outrow[colChar*3+1]= trp2[(inrow[colChar] >> 2) & 0x0F];
-            outrow[colChar*3+2]= trp3[inrow[colChar] & 0x07];
+            outrow[colChar*3]   = trp1[(inrow[colChar] & 0xF0) >> 5];
+            outrow[colChar*3+1] = trp2[(inrow[colChar] >> 2) & 0x0F];
+            outrow[colChar*3+2] = trp3[(inrow[colChar] >> 0) & 0x07];
         }
         break;  
+
+    case 4:
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned int i;
+            for (i = 0; i < 4; ++i) 
+                outrow[colChar*4+i]=
+                    quad[(inrow[colChar] >> (6 - 2 * i)) & 0x03]; 
+        }
+        break;
+
     case 5:
         for (colChar = 0; colChar < inColChars; ++colChar) { 
-            outrow[colChar*5]  = pair[ (inrow[colChar] >>6) & 0x03 ] >> 5; 
-            outrow[colChar*5+1]= quin2[(inrow[colChar] >>4) & 0x07 ]; 
-            outrow[colChar*5+2]= pair[ (inrow[colChar] >>3) & 0x03 ] >> 4; 
-            outrow[colChar*5+3]= quin4[(inrow[colChar] >>1) & 0x07 ];
-            outrow[colChar*5+4]= pair[ inrow[colChar] & 0x03 ] >>3; 
+            outrow[colChar*5]   = pair [(inrow[colChar] >> 6) & 0x03] >> 5; 
+            outrow[colChar*5+1] = quin2[(inrow[colChar] >> 4) & 0x07] >> 0; 
+            outrow[colChar*5+2] = quad [(inrow[colChar] >> 3) & 0x03] >> 0; 
+            outrow[colChar*5+3] = quin4[(inrow[colChar] >> 1) & 0x07] >> 0;
+            outrow[colChar*5+4] = pair [(inrow[colChar] >> 0) & 0x03] >> 3; 
+        }
+        break;
+
+    case 6:  /* Compound of 2 and 3 */ 
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            unsigned char const hi = dbl[(inrow[colChar] & 0xF0) >> 4];
+            unsigned char const lo = dbl[(inrow[colChar] & 0x0F) >> 0];
+
+            outrow[colChar*6]   = trp1[(hi & 0xF0) >> 5];
+            outrow[colChar*6+1] = trp2[(hi >> 2) & 0x0F];
+            outrow[colChar*6+2] = trp3[hi & 0x07];
+
+            outrow[colChar*6+3] = trp1[(lo & 0xF0) >> 5];
+            outrow[colChar*6+4] = trp2[(lo >> 2) & 0x0F];
+            outrow[colChar*6+5] = trp3[lo & 0x07];
+        }
+        break;  
+
+    case 7:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            uint32_t hi, lo;
+
+            hi = inrow[colChar] >> 4;
+            hi = ((((hi>>1) * 0x00082080) | (0x01 & hi)) & 0x00204081 ) * 0x7F;
+            hi >>= 4;
+            outrow[colChar*7]   =  (unsigned char) ( hi >> 16);
+            outrow[colChar*7+1] =  (unsigned char) ((hi >>  8) & 0xFF);
+            outrow[colChar*7+2] =  (unsigned char) ((hi >>  0) & 0xFF);
+
+            lo = inrow[colChar] & 0x001F;
+            lo = ((((lo>>1) * 0x02082080) | (0x01 & lo)) & 0x10204081 ) * 0x7F;
+            outrow[colChar*7+3] =  (unsigned char) ((lo >> 24) & 0xFF);
+            outrow[colChar*7+4] =  (unsigned char) ((lo >> 16) & 0xFF); 
+            outrow[colChar*7+5] =  (unsigned char) ((lo >>  8) & 0xFF);
+            outrow[colChar*7+6] =  (unsigned char) ((lo >>  0) & 0xFF);
+        }
+        break;
+
+    case 8:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            unsigned int i;
+            for (i = 0; i < 8; ++i) {
+                outrow[colChar*8+i] =
+                    ((inrow[colChar] >> (7-i)) & 0x01) *0xFF;
+            }
+        }
+        break;
+
+    case 9:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            outrow[colChar*9]   =  ((inrow[colChar] >> 7) & 0x01) * 0xFF;
+            outrow[colChar*9+1] =  pair[(inrow[colChar] >> 6) & 0x03] >> 1; 
+            outrow[colChar*9+2] =  pair[(inrow[colChar] >> 5) & 0x03] >> 2; 
+            outrow[colChar*9+3] =  pair[(inrow[colChar] >> 4) & 0x03] >> 3; 
+            outrow[colChar*9+4] =  pair[(inrow[colChar] >> 3) & 0x03] >> 4; 
+            outrow[colChar*9+5] =  pair[(inrow[colChar] >> 2) & 0x03] >> 5; 
+            outrow[colChar*9+6] =  pair[(inrow[colChar] >> 1) & 0x03] >> 6; 
+            outrow[colChar*9+7] =  pair[(inrow[colChar] >> 0) & 0x03] >> 7; 
+            outrow[colChar*9+8] =  (inrow[colChar] & 0x01) * 0xFF;
+        }
+        break;
+
+    case 10:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            outrow[colChar*10]   = ((inrow[colChar] >> 7) & 0x01 ) * 0xFF;
+            outrow[colChar*10+1] = pair[(inrow[colChar] >> 6) & 0x03] >> 2; 
+            outrow[colChar*10+2] = pair[(inrow[colChar] >> 5) & 0x03] >> 4; 
+            outrow[colChar*10+3] = pair[(inrow[colChar] >> 4) & 0x03] >> 6;
+            outrow[colChar*10+4] = ((inrow[colChar] >> 4) & 0x01) * 0xFF;
+            outrow[colChar*10+5] = ((inrow[colChar] >> 3) & 0x01) * 0xFF; 
+            outrow[colChar*10+6] = pair[(inrow[colChar] >> 2) & 0x03] >> 2; 
+            outrow[colChar*10+7] = pair[(inrow[colChar] >> 1) & 0x03] >> 4; 
+            outrow[colChar*10+8] = pair[(inrow[colChar] >> 0) & 0x03] >> 6; 
+            outrow[colChar*10+9] = ((inrow[colChar] >> 0) & 0x01) * 0xFF;
         }
         break;
-    case 4: default:
-        /*  Unlike the above cases, we iterate through outrow.  The color
-            composition of each outrow byte is computed by consulting
-            a single bit or two consecutive bits in inrow. 
+ 
+
+    default:
+        /*  Unlike the above cases, we iterate through outrow.  To compute the
+            color composition of each outrow byte, we consult a single bit or
+            two consecutive bits in inrow.
+
             Color changes never happen twice in a single outrow byte.
+
+            This is a generalization of above routines for scale factors
+            9 and 10.
+
+            Logic works for scale factors 4, 6, 7, 8, and above (but not 5).
         */
+
         for (colChar = 0; colChar < outColChars; ++colChar) {
             unsigned int const mult = scaleFactor;
             unsigned int const mod = colChar % mult;
@@ -199,12 +295,14 @@ enlargePbm(struct pam * const inpamP,
     
     if (scaleFactor == 1)
         outrow = inrow;
-    else  
-        outrow = pbm_allocrow_packed(outcols + 32);
-            /* The 32 (=4 bytes) is to allow writes beyond outrow data
-               end when scaleFactor is 2, 3, 5.  (max 4 bytes when
-               scaleFactor is 5)
-            */
+    else  {
+        /* Allow writes beyond outrow data end when scaleFactor is
+           one of the special fast cases: 2, 3, 4, 5, 6, 7, 8, 9, 10.
+        */
+        unsigned int const rightPadding = 
+            scaleFactor > 10 ? 0 : (scaleFactor - 1) * 8;  
+        outrow = pbm_allocrow_packed(outcols + rightPadding);
+    }
 
     pbm_writepbminit(ofP, outcols, outrows, 0);
     
@@ -214,7 +312,8 @@ enlargePbm(struct pam * const inpamP,
         pbm_readpbmrow_packed(inpamP->file, inrow, inpamP->width,
                               inpamP->format);
 
-        if (inpamP->width % 8 > 0) {  /* clean final partial byte */ 
+        if (outcols % 8 > 0) {
+            /* clean final partial byte */ 
             inrow[inColChars-1] >>= 8 - inpamP->width % 8;
             inrow[inColChars-1] <<= 8 - inpamP->width % 8;
         }
diff --git a/editor/pamflip.c b/editor/pamflip.c
index 61c6efce..83f776af 100644
--- a/editor/pamflip.c
+++ b/editor/pamflip.c
@@ -656,28 +656,44 @@ transformPbmGen(struct pam *     const inpamP,
             
     computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
     
-    bitrow = pbm_allocrow(inpamP->width);
-    newbits = pbm_allocarray(pbm_packed_bytes(outpamP->width), 
-                             outpamP->height);
+    bitrow = pbm_allocrow_packed(inpamP->width);
+    newbits = pbm_allocarray_packed( outpamP->width, outpamP->height );
             
     /* Initialize entire array to zeroes.  One bits will be or'ed in later */
     for (row = 0; row < outpamP->height; ++row) {
         unsigned int col;
         for (col = 0; col < pbm_packed_bytes(outpamP->width); ++col) 
-             newbits[row][col] = 0; 
+            newbits[row][col] = 0; 
     }
     
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int col;
-        pbm_readpbmrow(inpamP->file, bitrow, inpamP->width, inpamP->format);
-        for (col = 0; col < inpamP->width; ++col) {
-            unsigned int newcol, newrow;
-            transformPoint(col, row, xform, &newcol, &newrow);
-            newbits[newrow][newcol/8] |= bitrow[col] << (7 - newcol % 8);
-                /* Use of "|=" patterned after pbm_readpbmrow_packed. */
-         }
+
+        pbm_readpbmrow_packed(inpamP->file, bitrow,
+                              inpamP->width, inpamP->format);
+        for (col = 0; col < inpamP->width; ) {
+            if (bitrow[col/8] == 0x00) 
+                col += 8;  /* Blank.   Skip to next byte. */
+            else {      /* Examine each pixel. */
+                unsigned int const colLimit = MIN(col+8, inpamP->width);
+                unsigned int i;
+
+                for (i = 0; col < colLimit; ++i, ++col) {
+                    bool const bitIsOne = (bitrow[col/8] >> (7-i)) & 0x01;
+                    if (bitIsOne) {
+                        /* Write in only the one bits. */  
+                        unsigned int newcol, newrow;
+                        transformPoint(col, row, xform, &newcol, &newrow);
+                        newbits[newrow][newcol/8] |= 0x01 << (7 - newcol % 8);
+                            /* Use of "|=" patterned after
+                               pbm_readpbmrow_packed().
+                            */
+                    }
+                }
+            }
+        }
     }
-    
+
     for (row = 0; row < outpamP->height; ++row)
         pbm_writepbmrow_packed(outpamP->file, newbits[row], outpamP->width, 0);
     
diff --git a/editor/pampaintspill.c b/editor/pampaintspill.c
new file mode 100644
index 00000000..2b6535c1
--- /dev/null
+++ b/editor/pampaintspill.c
@@ -0,0 +1,386 @@
+/* ----------------------------------------------------------------------
+ *
+ * Bleed colors from non-background colors into the background
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <alloca.h>
+#include <time.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+#include "pammap.h"
+
+
+static time_t const timeUpdateDelta = 30;
+    /* Seconds between progress updates */
+static int const    minUpdates = 4;
+    /* Minimum number of progress updates to output */
+
+
+struct cmdlineInfo {
+    /* This structure represents all of the information the user
+       supplied in the command line but in a form that's easy for the
+       program to use.
+    */
+    const char * inputFilename;  /* '-' if stdin */
+    const char * bgcolor;
+    unsigned int wrap;
+    unsigned int all;
+    float        power;
+    unsigned int downsample;
+};
+
+struct coords {
+    /* This structure represents an (x,y) coordinate within an image. */
+    unsigned int x;
+    unsigned int y;
+};
+
+typedef double distFunc_t(struct coords const p0,
+                          struct coords const p1,
+                          unsigned int  const width,
+                          unsigned int  const height);
+    /* Distance function */
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+
+    optEntry     * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3     opt;
+    unsigned int   option_def_index;
+    unsigned int bgcolorSpec, powerSpec,downsampleSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    option_def_index = 0;          /* Incremented by OPTENTRY */
+
+    OPTENT3(0, "bgcolor",    OPT_STRING, &cmdlineP->bgcolor,    
+            &bgcolorSpec, 0);
+    OPTENT3(0, "wrap",       OPT_FLAG,   NULL,
+            &cmdlineP->wrap,       0);
+    OPTENT3(0, "all",        OPT_FLAG,   NULL,
+            &cmdlineP->all,        0);
+    OPTENT3(0, "power",      OPT_FLOAT,  &cmdlineP->power,      
+            &powerSpec, 0);
+    OPTENT3(0, "downsample", OPT_UINT,   &cmdlineP->downsample, 
+            &downsampleSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum = 1;
+
+    optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+
+    if (!bgcolorSpec)
+        cmdlineP->bgcolor = NULL;
+
+    if (!powerSpec)
+        cmdlineP->power = -2.0;
+
+    if (!downsampleSpec)
+        cmdlineP->downsample = 0;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilename = "-";
+    else {
+        cmdlineP->inputFilename = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only argument is the "
+                     "optional input file name", argc-1);
+    }
+}
+
+
+
+static void
+locatePaintSources(struct pam *     const pamP,
+                   tuple **         const tuples,
+                   tuple            const bgColor,
+                   unsigned int     const downsample,
+                   struct coords ** const paintSourcesP,
+                   unsigned int *   const numPaintSourcesP) {
+/*--------------------------------------------------------------------
+  Construct a list of all pixel coordinates in the input image that
+  represent a non-background color.
+  ----------------------------------------------------------------------*/
+    struct coords * paintSources;
+        /* List of paint-source indexes into inImage */
+    unsigned int numPaintSources;  /* Number of entries in the above */
+    unsigned int numAlloced;    /* Number of allocated coordinates. */
+    unsigned int row;
+
+    paintSources = NULL;
+    numAlloced = 0;
+    numPaintSources = 0;
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            if (!pnm_tupleequal(pamP, tuples[row][col], bgColor)) {
+                /* Add (row, col) to the list of paint sources. */
+                if (numPaintSources == numAlloced) {
+                    numAlloced += pamP->width;
+                    REALLOCARRAY(paintSources, numAlloced);
+                    if (!paintSources)
+                        pm_error("Out of memory");
+                }
+                paintSources[numPaintSources].x = col;
+                paintSources[numPaintSources].y = row;
+                ++numPaintSources;
+            }
+        }
+    }
+
+    pm_message("Image contains %u background + %u non-background pixels",
+               pamP->width * pamP->height - numPaintSources,
+               numPaintSources);
+    
+    /* Reduce the number of paint sources to reduce execution time. */
+    if (downsample > 0 && downsample < numPaintSources) {
+        unsigned int i;
+
+        srandom(time(NULL));
+
+        for (i = 0; i < downsample; ++i) {
+            unsigned int const swapIdx = i + random() % (numPaintSources - i);
+            struct coords const swapVal = paintSources[i];
+
+            paintSources[i] = paintSources[swapIdx];
+            paintSources[swapIdx] = swapVal;
+        }
+        numPaintSources = downsample;
+    }
+    *paintSourcesP    = paintSources;
+    *numPaintSourcesP = numPaintSources;
+}
+
+
+
+static distFunc_t euclideanDistanceSqr;
+
+static double
+euclideanDistanceSqr(struct coords const p0,
+                     struct coords const p1,
+                     unsigned int  const width,
+                     unsigned int  const height) {
+/*----------------------------------------------------------------------------
+   Return the square of the Euclidian distance between p0 and p1.
+-----------------------------------------------------------------------------*/
+    double const deltax = (double) (p1.x - p0.x);
+    double const deltay = (double) (p1.y - p0.y);
+
+    return SQR(deltax) + SQR(deltay);
+}
+
+
+
+static distFunc_t euclideanDistanceTorusSqr;
+
+static double
+euclideanDistanceTorusSqr(struct coords const p0,
+                          struct coords const p1,
+                          unsigned int  const width,
+                          unsigned int  const height) {
+/*----------------------------------------------------------------------------
+   Return the square of the Euclidian distance between p0 and p1, assuming
+   it's a toroidal surface on which the top row curves around to meet the
+   bottom and the left column to the right.
+-----------------------------------------------------------------------------*/
+    struct coords p0Adj, p1Adj;
+
+    if (p1.x >= p0.x + width / 2) {
+        p0Adj.x = p0.x + width;
+        p1Adj.x = p1.x;
+    } else if (p0.x >= p1.x + width / 2) {
+        p0Adj.x = p0.x;
+        p1Adj.x = p1.x + width;
+    } else {
+        p0Adj.x = p0.x;
+        p1Adj.x = p1.x;
+    }
+    if (p1.y >= p0.y + height / 2) {
+        p0Adj.y = p0.y + height;
+        p1Adj.y = p1.y;
+    } else if (p0.y >= p1.y + height / 2) {
+        p0Adj.y = p0.y;
+        p1Adj.y = p1.y + height;
+    } else {
+        p0Adj.y = p0.y;
+        p1Adj.y = p1.y;
+    }
+
+    return euclideanDistanceSqr(p0Adj, p1Adj, 0, 0);
+}
+
+
+
+static void
+reportProgress(unsigned int const rowsComplete,
+               unsigned int const height) {
+
+    static time_t prevOutputTime = 0;
+    time_t        now;                  /* Current time in seconds */
+
+    if (prevOutputTime == 0)
+        prevOutputTime = time(NULL);
+
+    /* Output our progress only every timeUpdateDelta seconds. */
+    now = time(NULL);
+    if (prevOutputTime) {
+        if (now - prevOutputTime >= timeUpdateDelta
+            || rowsComplete % (height/minUpdates) == 0) {
+            pm_message("%.1f%% complete",
+                       rowsComplete * 100.0 / height);
+            prevOutputTime = now;
+        }
+    } else
+        prevOutputTime = now;
+}
+
+
+
+static void
+produceOutputImage(struct pam *          const pamP,
+                   tuple **              const tuples,
+                   tuple                 const bgColor,
+                   const struct coords * const paintSources,
+                   unsigned int          const numPaintSources,
+                   distFunc_t *          const distFunc,
+                   double                const distPower,
+                   bool                  const all) {
+/*--------------------------------------------------------------------
+  Color each background pixel (or, if allPixels is 1, all pixels)
+  using a fraction of each paint source as determined by its distance
+  to the background pixel.
+----------------------------------------------------------------------*/
+    struct coords target;
+
+    for (target.y = 0; target.y < pamP->height; ++target.y) {
+        double * newColor;
+        
+        MALLOCARRAY(newColor, pamP->depth);
+
+        for (target.x = 0; target.x < pamP->width; ++target.x) {
+            if (all ||
+                pnm_tupleequal(pamP, tuples[target.y][target.x], bgColor)) {
+
+                unsigned int plane;
+                unsigned int ps;
+                double       totalWeight;
+
+                for (plane = 0; plane < pamP->depth; ++plane)
+                    newColor[plane] = 0.0;
+                totalWeight = 0.0;
+                for (ps = 0; ps < numPaintSources; ++ps) {
+                    struct coords const source = paintSources[ps];
+                    tuple const paintColor = tuples[source.y][source.x];
+                    double const distSqr =
+                        (*distFunc)(target, source,
+                                    pamP->width, pamP->height);
+
+                    if (distSqr > 0.0) {
+                        /* We do special cases for some common cases with code
+                           that is much faster than pow().
+                        */
+                        double const weight =
+                            distPower == -2.0 ? 1.0 / distSqr :
+                            distPower == -1.0 ? 1.0 / sqrt(distSqr):
+                            pow(distSqr, distPower/2);
+
+                        unsigned int plane;
+
+                        for (plane = 0; plane < pamP->depth; ++plane)
+                            newColor[plane] += weight * paintColor[plane];
+
+                        totalWeight += weight;
+                    }
+                }
+                for (plane = 0; plane < pamP->depth; ++plane)
+                    tuples[target.y][target.x][plane] =
+                        (sample) (newColor[plane] / totalWeight);
+            }
+        }
+        reportProgress(target.y, pamP->height);
+
+        free(newColor);
+    }
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    FILE *             ifP;
+    struct cmdlineInfo cmdline;          /* Command-line parameters */
+    tuple              bgColor;          /* Input image's background color */
+    struct coords *    paintSources;
+        /* List of paint-source indexes into inImage */
+    unsigned int       numPaintSources;  /* Number of entries in the above */
+    distFunc_t *       distFunc;         /* The distance function */
+    struct pam inpam;
+    struct pam outPam;
+    tuple ** tuples;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pm_close(ifP);
+
+    distFunc = cmdline.wrap ? euclideanDistanceTorusSqr : euclideanDistanceSqr;
+
+    if (cmdline.bgcolor)
+        bgColor = pnm_parsecolor(cmdline.bgcolor, inpam.maxval) ;
+    else
+        bgColor = pnm_backgroundtuple(&inpam, tuples);
+
+    pm_message("Treating %s as the background color",
+               pnm_colorname(&inpam, bgColor, PAM_COLORNAME_HEXOK));
+
+    locatePaintSources(&inpam, tuples, bgColor, cmdline.downsample,
+                       &paintSources, &numPaintSources);
+
+    produceOutputImage(&inpam, tuples,
+                       bgColor, paintSources, numPaintSources, distFunc,
+                       cmdline.power, cmdline.all);
+
+
+    outPam = inpam;
+    outPam.file = stdout;
+    pnm_writepam(&outPam, tuples);
+
+    return 0;
+}
diff --git a/editor/pbmclean.c b/editor/pbmclean.c
index 65b53e1c..beecf3e8 100644
--- a/editor/pbmclean.c
+++ b/editor/pbmclean.c
@@ -1,76 +1,62 @@
-/* pbmclean.c - pixel cleaning. Remove pixel if less than n connected
- *              identical neighbours, n=1 default.
- * AJCD 20/9/90
- * stern, Fri Oct 19 00:10:38 MET DST 2001
- *     add '-white/-black' flags to restrict operation to given blobs
- */
+/*=============================================================================
+                                 pbmclean
+===============================================================================
+  Pixel cleaner:   Remove pixel if less than N connected identical neighbors
 
+=============================================================================*/
+#include <assert.h>
 #include <stdio.h>
 
 #include "pm_c_util.h"
-#include "pbm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pbm.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input files */
+    const char * inputFileName;  /* File name of input file */
     bool flipWhite;
     bool flipBlack;
     unsigned int connect;
     unsigned int verbose;
 };
 
-#define PBM_INVERT(p) ((p) == PBM_WHITE ? PBM_BLACK : PBM_WHITE)
-
-/* input bitmap size and storage */
-static bit *inrow[3] ;
 
-#define THISROW (1)
-
-enum compass_heading {
-    WEST=0,
-    NORTHWEST=1,
-    NORTH=2,
-    NORTHEAST=3,
-    EAST=4,
-    SOUTHEAST=5,
-    SOUTH=6,
-    SOUTHWEST=7
-};
-/* compass directions from west clockwise.  Indexed by enum compass_heading */
-int const xd[] = { -1, -1,  0,  1, 1, 1, 0, -1 } ;
-int const yd[] = {  0, -1, -1, -1, 0, 1, 1,  1 } ;
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optStruct3 opt;  /* set by OPTENT3 */
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
     unsigned int option_def_index;
 
     unsigned int black, white;
     unsigned int minneighborsSpec;
 
+    MALLOCARRAY(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0);
-    OPTENT3(0,   "black", OPT_FLAG, NULL, &black, 0);
-    OPTENT3(0,   "white", OPT_FLAG, NULL, &white, 0);
-    OPTENT3(0,   "minneighbors", OPT_UINT, &cmdlineP->connect, 
+    OPTENT3(0,   "verbose",          OPT_FLAG, NULL, &cmdlineP->verbose, 0);
+    OPTENT3(0,   "black",            OPT_FLAG, NULL, &black, 0);
+    OPTENT3(0,   "white",            OPT_FLAG, NULL, &white, 0);
+    OPTENT3(0,   "minneighbors",     OPT_UINT, &cmdlineP->connect, 
             &minneighborsSpec, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We sort of allow negative numbers as parms */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free(option_def);
+
     if (!black && !white) {
         cmdlineP->flipBlack = TRUE;
         cmdlineP->flipWhite = TRUE;
@@ -79,7 +65,6 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->flipWhite = !!white;
     }    
 
-
     if (!minneighborsSpec) {
         /* Now we do a sleazy tour through the parameters to see if
            one is -N where N is a positive integer.  That's for
@@ -111,9 +96,9 @@ parseCommandLine(int argc, char ** argv,
     }
 
     if (argc-1 < 1) 
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else
         pm_error("You specified too many arguments (%d).  The only "
                  "argument is the optional input file specification.",
@@ -121,121 +106,323 @@ parseCommandLine(int argc, char ** argv,
 }
 
 
+static unsigned int
+bitpop8(unsigned char const x) {
+/*----------------------------------------------------------------------------
+   Return the number of 1 bits in 'x'
+-----------------------------------------------------------------------------*/
+static unsigned int const p[256] = {
+    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };
+
+    return p[x];
+}
 
 
 
-static void 
-nextrow(FILE * const ifd,
-        int    const row,
-        int    const cols,
-        int    const rows,
-        int    const format) {
+static unsigned int
+bitpop24(uint32_t const w){
 /*----------------------------------------------------------------------------
-   Advance one row in the input.
+   Return the number of 1 bits in the lower 24 bits of 'w'
+   A GCC builtin, __builtin_popcountl(), is available, but it
+   emits a call to an external function instead of inlining (GCC 4.4.3).
 
-   'row' is the row number that will be the current row.
+   This table lookup method is faster.
 -----------------------------------------------------------------------------*/
-    bit * shuffle;
-
-    /* First, get the "next" row in inrow[2] if this is the very first
-       call to nextrow().
-    */
-    if (inrow[2] == NULL && row < rows) {
-        inrow[2] = pbm_allocrow(cols);
-        pbm_readpbmrow(ifd, inrow[2], cols, format);
-    }
-    /* Now advance the inrow[] window, rotating the buffer that now holds
-       the "previous" row to use it for the new "next" row.
-    */
-    shuffle = inrow[0];
-
-    inrow[0] = inrow[1];
-    inrow[1] = inrow[2];
-    inrow[2] = shuffle ;
-    if (row+1 < rows) {
-        /* Read the "next" row in from the file.  Allocate buffer if needed */
-        if (inrow[2] == NULL)
-            inrow[2] = pbm_allocrow(cols);
-        pbm_readpbmrow(ifd, inrow[2], cols, format);
-    } else {
-        /* There is no next row */
-        if (inrow[2]) {
-            pbm_freerow(inrow[2]);
-            inrow[2] = NULL; 
-        }
-    }
+    return (bitpop8((w >> 16) & 0xff) +
+            bitpop8((w >>  8) & 0xff) +
+            bitpop8((w >>  0) & 0xff));  
 }
 
 
 
+/*----------------------------------------------------------------------------
+Fast algorithm for counting friendly neighbor pixels
+
+In this program both input and output rows are in raw (packed) PBM format.
+
+We handle input rows in groups of three, named "prevrow", "thisrow",
+"nextrow" and scan from left to right.  At every byte boundary, 10 bits
+are read from each of the three rows and placed into a temporary storage
+we call "sample".
+
+prevrow: ... ... _______M NNNNNNNN O_______ ...
+thisrow: ... ... _______W cCCCCCCC E_______ ...
+nextrow: ... ... _______R SSSSSSSS T_______ ...
+
+sample : xxMNNNNNNNNOWcCCCCCCCERSSSSSSST
+
+We count bits by taking the logical and of "sample" and a bit-mask called
+"selection", and feeding the result to a table-based bit-population counter.
+
+For example, the bits around the leftmost bit of the byte ("c") are selected
+like this:
+
+sample :       xxMNNNNNNNNOWcCCCCCCCERSSSSSSST
+selection: & | __111_______1_1_______111______
+
+(In the actual process, "sample" is shifted right and anded against a
+ constant "selection" mask.)
+
+The above reports one bits.  For the zero (white) bits we replace "sample"
+with its inverse.
+
+If the friendly neighbor count is below a threshold (default 1), we record
+that as a one bit in "flipmask".  Bits are flipped in units of eight
+and written to outrow at the byte boundary.
+-----------------------------------------------------------------------------*/
+
+
+
 static unsigned int
-likeNeighbors(bit *        const inrow[3], 
-              unsigned int const col, 
-              unsigned int const cols) {
+likeNeighbors(uint32_t     const blackSample, 
+              unsigned int const offset) {
+
+    bool const thispoint = ( blackSample >> (18-offset) ) & 0x01;
+    uint32_t const sample = (thispoint == PBM_BLACK ) 
+                          ?   blackSample
+                          : ~ blackSample ;
+    uint32_t const selection = 0x701407;
+
+    return (bitpop24((sample >> (7-offset)) & selection));
+}
+
+
+
+static uint32_t
+setSample(const bit *  const prevrow,
+          const bit *  const thisrow,
+          const bit *  const nextrow,
+          unsigned int const col){
+
+    int const col8 = col/8;
+
+    uint32_t sample;
+
+    sample =
+        ((prevrow[col8 - 1]       )  << 29) |
+        ((prevrow[col8]           )  << 21) |
+        ((prevrow[col8 + 1] & 0x80)  << 13) |
+        ((thisrow[col8 - 1] & 0x01)  << 19) |
+        ((thisrow[col8]           )  << 11) |
+        ((thisrow[col8 + 1] & 0x80)  <<  3) |
+        ((nextrow[col8 - 1] & 0x01)  <<  9) |
+        ((nextrow[col8]           )  <<  1) |
+        ((nextrow[col8 + 1] & 0x80)  >>  7);
     
-    int const point = inrow[THISROW][col];
-    enum compass_heading heading;
-    int joined;
-
-    joined = 0;  /* initial value */
-    for (heading = WEST; heading <= SOUTHWEST; ++heading) {
-        int x = col + xd[heading] ;
-        int y = THISROW + yd[heading] ;
-        if (x < 0 || x >= cols || !inrow[y]) {
-            if (point == PBM_WHITE) joined++;
-        } else if (inrow[y][x] == point) joined++ ;
-    }
-    return joined;
+    return sample;
 }
 
 
 
-int
-main(int argc, char *argv[]) {
+static unsigned char
+setTestmask(unsigned char const whiteTestmask,
+            bool          const testWhite,
+            bool          const testBlack) {
+/* -----------------------------------------------------------------------
+  Make a byte pattern of what bits should be tested within a given "thisrow"
+  (current inrow) byte.  0 means test, 1 means skip.
+-------------------------------------------------------------------------- */
+    if (testWhite == testBlack) {
+        assert(testWhite); assert(testBlack);
+        return 0x00;
+    } else if (testWhite == TRUE) {
+        assert(!testBlack);
+        return whiteTestmask;
+    } else
+        return ~whiteTestmask;
+}
 
-    struct cmdlineInfo cmdline;
-    FILE *ifp;
-    bit *outrow;
+
+
+static void
+cleanrow(const bit *    const prevrow,
+         const bit *    const thisrow,
+         const bit *    const nextrow,
+         bit *          const outrow,
+         unsigned int   const cols,
+         unsigned int   const threshold,
+         bool           const flipWhite,
+         bool           const flipBlack,
+         unsigned int * const nFlippedP) {
+/* ----------------------------------------------------------------------
+  Work through row, scanning for bits that require flipping, and write
+  the results to outrow.
+  
+  Returns the number of bits flipped within this one row as *nFlippedP.
+-------------------------------------------------------------------------*/
+    uint32_t sample;
+    unsigned char testmask;
+    unsigned char flipmask;
+    unsigned int col;
+    unsigned int nFlipped;
+
+    flipmask = 0x00;  /* initial value */
+    nFlipped = 0;     /* initial value */
+
+    for (col=0 ; col < cols ; ++col) {
+        unsigned int const col8 = col / 8;
+        unsigned int const offset = col % 8;
+
+        if (offset == 0) {
+            if (flipmask != 0x00) {
+                /* Some bits have to be flipped */
+                outrow[col8 -1] = thisrow [col8 -1] ^ flipmask;
+                nFlipped += bitpop8(flipmask);
+                flipmask = 0x00;
+            } else if (col8 > 0)
+                outrow[col8 -1] = thisrow [col8 -1];
+
+            sample = setSample(prevrow, thisrow, nextrow, col);
+            testmask = setTestmask(thisrow[col8], flipWhite, flipBlack);
+        }
+
+        if (((testmask << offset) & 0x80 ) ==0) {
+            if (likeNeighbors(sample, offset ) < threshold)
+                flipmask |= (0x80 >> offset);
+        }
+    }
+
+    {
+        /* Write out last byte */
+        unsigned int const col8Last = pbm_packed_bytes(cols) -1;
+
+        if (flipmask != 0x00) {
+            outrow[col8Last] = thisrow[col8Last] ^ flipmask;
+            nFlipped += bitpop8(flipmask);
+        } else
+            outrow[col8Last] = thisrow[col8Last];
+    }
+    *nFlippedP = nFlipped;
+}
+
+
+
+static void
+pbmclean(FILE *             const ifP,
+         FILE *             const ofP,
+         struct cmdlineInfo const cmdline,
+         double *           const nFlippedP) {
+
+    bit ** buffer;
+    bit * prevrow;
+    bit * thisrow;
+    bit * nextrow;
+    bit * outrow;
+    bit * edgerow;
     int cols, rows, format;
     unsigned int row;
-    unsigned int nFlipped;  /* Number of pixels we have flipped so far */
 
-    pbm_init( &argc, argv );
+    pbm_readpbminit(ifP, &cols, &rows, &format);
 
-    parseCommandLine(argc, argv, &cmdline);
+    /* Initialize input buffers.
+       We add a margin of 8 bits each on the left and right of the rows.
 
-    ifp = pm_openr(cmdline.inputFilespec);
+       On the top and bottom of the image we place an imaginary blank row
+       ("edgerow") to facilitate the process.
+    */
+    {
+        unsigned int i;
+
+        buffer  = pbm_allocarray_packed(cols+16, 3);
+        edgerow = pbm_allocrow_packed(cols+16);
+
+        for (i = 0; i < pbm_packed_bytes(cols+16); ++i)
+            edgerow[i] = 0x00;
+        
+        for (i = 0; i < 3; ++i) {
+            /* Add blank (all white) bytes beside the edges */ 
+            buffer[i][0] = buffer[i][ pbm_packed_bytes( cols +16 ) - 1] = 0x00;
+        }
+        thisrow = &edgerow[1];
+        nextrow = &buffer[0][1];
 
-    inrow[0] = inrow[1] = inrow[2] = NULL;
-    pbm_readpbminit(ifp, &cols, &rows, &format);
+        /* 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(cols);
 
-    pbm_writepbminit(stdout, cols, rows, 0) ;
+    pbm_writepbminit(ofP, cols, rows, 0) ;
+
+    *nFlippedP = 0;  /* none flipped yet */
 
-    nFlipped = 0;  /* No pixels flipped yet */
     for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        nextrow(ifp, row, cols, rows, format);
-        for (col = 0; col < cols; ++col) {
-            bit const thispoint = inrow[THISROW][col];
-            if ((cmdline.flipWhite && thispoint == PBM_WHITE) ||
-                (cmdline.flipBlack && thispoint == PBM_BLACK)) {
-                if (likeNeighbors(inrow, col, cols) < cmdline.connect) {
-                    outrow[col] = PBM_INVERT(thispoint);
-                    ++nFlipped;
-                } else
-                    outrow[col] = thispoint;
-            } else 
-                outrow[col] = thispoint;
-        }
-        pbm_writepbmrow(stdout, outrow, cols, 0) ;
+        unsigned int nFlipped;
+
+        prevrow = thisrow;  /* Slide up the input row window */
+        thisrow = nextrow;
+        if (row < rows -1){
+            nextrow = &buffer[(row+1)%3][1];
+            /* We take the address directly instead of shuffling the rows
+               with the help of a temporary.  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[1];
+
+        cleanrow(prevrow, thisrow, nextrow, outrow, cols, cmdline.connect,
+                 cmdline.flipWhite, cmdline.flipBlack, &nFlipped);
+        
+        *nFlippedP += nFlipped;
+
+        pbm_writepbmrow_packed(ofP, outrow, cols, 0) ;
     }
+
+    pbm_freearray(buffer, 3);
+    pbm_freerow(edgerow);
     pbm_freerow(outrow);
-    pm_close(ifp);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    double nFlipped;
+        /* Number of pixels we have flipped so far.  Use type double to
+           prevent overflow.
+        */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbmclean(ifP, stdout, cmdline, &nFlipped);
 
     if (cmdline.verbose)
-        pm_message("%d pixels flipped", nFlipped);
+        pm_message("%f pixels flipped", nFlipped);
+
+    pm_close(ifP);
 
     return 0;
 }
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;
 }
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index b1f4adc8..c29a6fe4 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -66,6 +66,7 @@ struct cmdlineInfo {
     const char ** matrixfile;
     unsigned int matrixSpec;
     struct matrixOpt matrix;
+    unsigned int normalize;
 };
 
 
@@ -313,6 +314,8 @@ parseCommandLine(int argc, char ** argv,
             &matrixfileSpec,           0)
     OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,                  
             &cmdlineP->nooffset,       0);
+    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
+            &cmdlineP->normalize,      0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -528,6 +531,52 @@ convKernelDestroy(struct convKernel * const convKernelP) {
 
 
 static void
+normalizeKernelPlane(struct convKernel * const convKernelP,
+                     unsigned int        const plane) {
+
+    unsigned int row;
+    float sum;
+
+    for (row = 0, sum = 0.0; row < convKernelP->rows; ++row) {
+        unsigned int col;
+            
+        for (col = 0; col < convKernelP->cols; ++col) {
+                
+            sum += convKernelP->weight[plane][row][col];
+        }
+    }
+
+    {
+        float const scaler = 1.0/sum;
+
+        unsigned int row;
+
+        for (row = 0; row < convKernelP->rows; ++row) {
+            unsigned int col;
+                
+            for (col = 0; col < convKernelP->cols; ++col)
+                convKernelP->weight[plane][row][col] *= scaler;
+        }
+    }
+}
+
+
+
+static void
+normalizeKernel(struct convKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+   Modify *convKernelP by scaling every weight in a plane by the same factor
+   such that the weights in the plane all add up to 1.
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+
+    for (plane = 0; plane < convKernelP->planes; ++plane)
+        normalizeKernelPlane(convKernelP, plane);
+}
+
+
+
+static void
 getKernelPnm(const char *         const fileName,
              unsigned int         const depth,
              bool                 const nooffset,
@@ -552,9 +601,20 @@ getKernelPnm(const char *         const fileName,
 
 static void
 convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
+                          bool                 const normalize,
                           unsigned int         const depth,
                           struct convKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Create a convolution kernel as described by a -matrix command line
+   option.
+   
+   The option value is 'matrixOpt'.
 
+   If 'normalize' is true, we normalize whatever numbers the option specifies
+   so that they add up to one; otherwise, we take the numbers as we find them,
+   so they may form a biased matrix -- i.e. one which brightens or dims the
+   image overall.
+-----------------------------------------------------------------------------*/
     struct convKernel * convKernelP;
     unsigned int plane;
 
@@ -579,6 +639,9 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
                     matrixOpt.weight[row][col];
         }
     }
+    if (normalize)
+        normalizeKernel(convKernelP);
+
     *convKernelPP = convKernelP;
 }
 
@@ -742,9 +805,21 @@ copyWeight(float **       const srcWeight,
 
 static void
 convKernelCreateSimpleFile(const char **        const fileNameList,
+                           bool                 const normalize,
                            unsigned int         const depth,
                            struct convKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Create a convolution kernel as described by a convolution matrix file.
+   This is the simple file with floating point numbers in it, not the
+   legacy pseudo-PNM thing.
+
+   The name of the file is 'fileNameList'.
 
+   If 'normalize' is true, we normalize whatever numbers we find in the file
+   so that they add up to one; otherwise, we take the numbers as we find them,
+   so they may form a biased matrix -- i.e. one which brightens or dims the
+   image overall.
+-----------------------------------------------------------------------------*/
     struct convKernel * convKernelP;
     unsigned int fileCt;
     unsigned int planeCt;
@@ -792,6 +867,10 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
                        &convKernelP->weight[plane]);
         }
     }
+
+    if (normalize)
+        normalizeKernel(convKernelP);
+
     convKernelP->cols = width;
     convKernelP->rows = height;
     *convKernelPP = convKernelP;
@@ -803,16 +882,25 @@ static void
 getKernel(struct cmdlineInfo   const cmdline,
           unsigned int         const depth,
           struct convKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Figure out what the convolution kernel is.  It can come from various
+   sources in various forms, as described on the command line, represented
+   by 'cmdline'.
 
+   We generate a kernel object in standard form (free of any indication of
+   where it came from) and return a handle to it as *convKernelPP.
+-----------------------------------------------------------------------------*/
     struct convKernel * convKernelP;
 
     if (cmdline.pnmMatrixFileName)
         getKernelPnm(cmdline.pnmMatrixFileName, depth, cmdline.nooffset,
                      &convKernelP);
     else if (cmdline.matrixfile)
-        convKernelCreateSimpleFile(cmdline.matrixfile, depth, &convKernelP);
+        convKernelCreateSimpleFile(cmdline.matrixfile, cmdline.normalize,
+                                   depth, &convKernelP);
     else if (cmdline.matrixSpec)
-        convKernelCreateMatrixOpt(cmdline.matrix, depth, &convKernelP);
+        convKernelCreateMatrixOpt(cmdline.matrix, cmdline.normalize,
+                                  depth, &convKernelP);
 
     warnBadKernel(convKernelP);
 
@@ -1665,7 +1753,7 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
             outputrow[col][plane] = window[crowso2][col][plane];
         else if (col == ccolso2) {
             unsigned int const leftcol = 0;
-                // Window is up againt left edge of image
+                /* Window is up againt left edge of image */
 
             float matrixSum;
 
@@ -1829,12 +1917,12 @@ convolveVerticalRowPlane(struct pam *              const pamP,
             outputrow[col][plane] = circMap[crowso2][col][plane];
         else if (col == ccolso2) {
             unsigned int const leftcol = 0;
-                // Convolution window is againt left edge of image
+                /* Convolution window is againt left edge of image */
 
             float matrixSum;
             unsigned int ccol;
 
-            // Slide window down in the first kernel's worth of columns
+            /* Slide window down in the first kernel's worth of columns */
             for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
                 convColumnSum[leftcol + ccol] +=
                     circMap[addrow][leftcol + ccol][plane];
@@ -1854,7 +1942,7 @@ convolveVerticalRowPlane(struct pam *              const pamP,
             float matrixSum;
             unsigned int ccol;
 
-            // Slide window down in the column that just entered the window
+            /* Slide window down in the column that just entered the window */
             convColumnSum[addcol] += circMap[addrow][addcol][plane];
             convColumnSum[addcol] -= circMap[subrow][addcol][plane];
 
diff --git a/editor/pnmhisteq.c b/editor/pnmhisteq.c
index 1987efc3..97dbe472 100644
--- a/editor/pnmhisteq.c
+++ b/editor/pnmhisteq.c
@@ -144,7 +144,7 @@ computeLuminosityHistogram(xel * const *   const xels,
             for (col = 0; col < cols; ++col) {
                 xel const thisXel = xels[row][col];
                 if (!monoOnly || PPM_ISGRAY(thisXel)) {
-                    xelval const l = PPM_LUMIN(thisXel);
+                    xelval const l = ppm_luminosity(thisXel);
 
                     lmin = MIN(lmin, l);
                     lmax = MAX(lmax, l);
@@ -302,6 +302,48 @@ reportMap(const unsigned int * const lumahist,
 
 
 
+static xel
+scaleXel(xel    const thisXel,
+         double const scaler) {
+/*----------------------------------------------------------------------------
+   Scale the components of 'xel' by multiplying by 'scaler'.
+
+   Assume this doesn't cause it to exceed maxval.
+-----------------------------------------------------------------------------*/
+    xel retval;
+
+    PNM_ASSIGN(retval,
+               ((xelval)(PNM_GETR(thisXel) * scaler + 0.5)),
+               ((xelval)(PNM_GETG(thisXel) * scaler + 0.5)),
+               ((xelval)(PNM_GETB(thisXel) * scaler + 0.5)));
+    
+    return retval;
+}
+
+
+
+static xel
+remapRgbValue(xel          const thisXel,
+              xelval       const maxval,
+              const gray * const lumamap) {
+/*----------------------------------------------------------------------------
+  Return the color 'thisXel' with its HSV value changed per 'lumamap' but
+  the same hue and saturation.
+
+  'maxval' is the maxval for 'xel' and our return value.
+-----------------------------------------------------------------------------*/
+    struct hsv const hsv =
+        ppm_hsv_from_color(thisXel, maxval);
+    xelval const oldValue =
+        MIN(maxval, ROUNDU(hsv.v * maxval));
+    xelval const newValue =
+        lumamap[oldValue];
+    
+    return scaleXel(thisXel, (double)newValue/oldValue);
+}
+
+
+
 static void
 remap(xel **       const xels,
       unsigned int const cols,
@@ -323,16 +365,8 @@ remap(xel **       const xels,
                 if (monoOnly && PPM_ISGRAY(thisXel)) {
                     /* Leave this pixel alone */
                 } else {
-                    struct hsv hsv;
-                    xelval iv;
-
-                    hsv = ppm_hsv_from_color(thisXel, maxval);
-                    iv = MIN(maxval, ROUNDU(hsv.v * maxval));
-                    
-                    hsv.v = MIN(1.0, 
-                                ((double) lumamap[iv]) / ((double) maxval));
-
-                    xels[row][col] = ppm_color_from_hsv(hsv, maxval);
+                    xels[row][col] = remapRgbValue(xels[row][col],
+                                                   maxval, lumamap);
                 }
             }
         }
diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c
index 27d51115..36604dc9 100644
--- a/editor/pnmnorm.c
+++ b/editor/pnmnorm.c
@@ -211,7 +211,7 @@ buildHistogram(FILE *   const ifp,
             if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
                 switch(brightMethod) {
                 case BRIGHT_LUMINOSITY:
-                    brightness = PPM_LUMIN(p);
+                    brightness = ppm_luminosity(p);
                     break;
                 case BRIGHT_COLORVALUE:
                     brightness = ppm_colorvalue(p);
@@ -589,7 +589,7 @@ brightScaler(xel               const p,
              
     switch (brightMethod) {
     case BRIGHT_LUMINOSITY:
-        oldBrightness = PPM_LUMIN(p);
+        oldBrightness = ppm_luminosity(p);
         break;
     case BRIGHT_COLORVALUE:
         oldBrightness = ppm_colorvalue(p);
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index 5b58addb..29e05905 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -30,6 +30,7 @@
 #include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
+#include "ppm.h"
 #include "pammap.h"
 
 #define MAXCOLORS 32767u
@@ -1122,7 +1123,7 @@ getSpecifiedMissingColor(struct pam * const pamP,
             specColor[PAM_GRN_PLANE] = PPM_GETG(color);
             specColor[PAM_BLU_PLANE] = PPM_GETB(color);
         } else if (pamP->depth == 1) {
-            specColor[0] = PPM_LUMIN(color);
+            specColor[0] = ppm_luminosity(color);
         } else {
             pm_error("You may not use -missing with a colormap that is not "
                      "of depth 1 or 3.  Yours has depth %u",
diff --git a/editor/ppmdist.c b/editor/ppmdist.c
index 90c2e3d3..bca96adc 100644
--- a/editor/ppmdist.c
+++ b/editor/ppmdist.c
@@ -125,7 +125,7 @@ main(int argc, char *argv[]) {
          * by frequency - but again, for a small number of colors
          * it's a small matter.
          */
-        colorToGrayMap[color].gray = PPM_LUMIN(hist[color].color);
+        colorToGrayMap[color].gray = ppm_luminosity(hist[color].color);
     }
 
     /*
diff --git a/generator/pbmpage.c b/generator/pbmpage.c
index fcf7af42..a2f47bcc 100644
--- a/generator/pbmpage.c
+++ b/generator/pbmpage.c
@@ -17,6 +17,7 @@
 #include <math.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pbm.h"
 
 /* US is 8.5 in by 11 in */
@@ -31,10 +32,9 @@
 
 
 struct bitmap {
-    int Width;      /* width and height in 600ths of an inch */
-    int Height;
-    int Pwidth;     /* width in bytes */
-    char *bitmap;
+    unsigned int Width;      /* width and height in 600ths of an inch */
+    unsigned int Height;
+    bit ** bitmap;
 };
 
 static struct bitmap bitmap;
@@ -42,11 +42,10 @@ static struct bitmap bitmap;
 
 
 static void
-setpixel(int const x,
-         int const y,
-         int const c) {
+setpixel(unsigned int const x,
+         unsigned int const y,
+         unsigned int const c) {
 
-    int const charidx = y * bitmap.Pwidth + x/8;
     char const bitmask = 128 >> (x % 8);
 
     if (x < 0 || x >= bitmap.Width)
@@ -55,151 +54,148 @@ setpixel(int const x,
         return;
 
     if (c)
-        bitmap.bitmap[charidx] |= bitmask;
+        bitmap.bitmap[y][x/8] |= bitmask;
     else
-        bitmap.bitmap[charidx] &= ~bitmask;
+        bitmap.bitmap[y][x/8] &= ~bitmask;
 }
 
 
 
 static void 
-setplus(int x,int y,int s)
+setplus(unsigned int const x,
+        unsigned int const y,
+        unsigned int const s) {
 /*----------------------------------------------------------------------------
    Draw a black plus sign centered at (x,y) with arms 's' pixels long.  
    Leave the exact center of the plus white.
 -----------------------------------------------------------------------------*/
-{
-  int i;
-
-  for(i=0; i<s; i++)
-  {
-    setpixel(x+i,y,1);
-    setpixel(x-i,y,1);
-    setpixel(x,y+i,1);
-    setpixel(x,y-i,1);
-  }
+    unsigned int i;
+
+    for (i = 0; i < s; ++i) {
+        setpixel(x+i, y,   1);
+        setpixel(x-i, y,   1);
+        setpixel(x,   y+i, 1);
+        setpixel(x,   y-i, 1);
+    }
 }
 
 
 
 static void 
-setblock(int x,int y,int s)
-{
-  int i,j;
+setblock(unsigned int const x,
+         unsigned int const y,
+         unsigned int const s) {
+
+    unsigned int i;
+
+    for (i = 0; i < s; ++i) {
+        unsigned int j;
 
-  for(i=0; i<s; i++)
-    for(j=0; j<s; j++)
-      setpixel(x+i,y+j,1);
+        for (j = 0; j < s; ++j)
+            setpixel(x+i, y+j, 1);
+    }
 }
 
 
 
 static void 
-setchar(int x,int y,char c)
-{
-  int xo,yo;
-  static char charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e },
-				{ 0x00, 0x42, 0x7f, 0x40, 0x00 },
-				{ 0x42, 0x61, 0x51, 0x49, 0x46 },
-				{ 0x22, 0x41, 0x49, 0x49, 0x36 },
-				{ 0x18, 0x14, 0x12, 0x7f, 0x10 },
-				{ 0x27, 0x45, 0x45, 0x45, 0x39 },
-				{ 0x3e, 0x49, 0x49, 0x49, 0x32 },
-				{ 0x01, 0x01, 0x61, 0x19, 0x07 },
-				{ 0x36, 0x49, 0x49, 0x49, 0x36 },
-				{ 0x26, 0x49, 0x49, 0x49, 0x3e } };
-
-  if(c<='9' && c>='0')
-    for(xo=0; xo<5; xo++)
-      for(yo=0; yo<8; yo++)
-	if((charmap[c-'0'][xo]>>yo)&1)
-	  setblock(x+xo*3,y+yo*3,3);
+setchar(unsigned int const x,
+        unsigned int const y,
+        char         const c) {
+
+    static char const charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e },
+                                        { 0x00, 0x42, 0x7f, 0x40, 0x00 },
+                                        { 0x42, 0x61, 0x51, 0x49, 0x46 },
+                                        { 0x22, 0x41, 0x49, 0x49, 0x36 },
+                                        { 0x18, 0x14, 0x12, 0x7f, 0x10 },
+                                        { 0x27, 0x45, 0x45, 0x45, 0x39 },
+                                        { 0x3e, 0x49, 0x49, 0x49, 0x32 },
+                                        { 0x01, 0x01, 0x61, 0x19, 0x07 },
+                                        { 0x36, 0x49, 0x49, 0x49, 0x36 },
+                                        { 0x26, 0x49, 0x49, 0x49, 0x3e } };
+    
+    if (c <= '9' && c >= '0') {
+        unsigned int xo;
+
+        for (xo = 0; xo < 5; ++xo) {
+            unsigned int yo;
+
+            for (yo = 0; yo < 8; ++yo) {
+                if ((charmap[c-'0'][xo] >> yo) & 0x01)
+                    setblock(x + xo*3, y + yo*3, 3);
+            }
+        }
+    }
 }
 
 
 
 static void 
-setstring(int x,int y,char* s)
-{
-  char* p;
-  int xo;
+setstring(unsigned int const x,
+          unsigned int const y,
+          const char * const s) {
 
-  for(xo=0, p=s; *p; xo+=21, p++)
-    setchar(x+xo,y,*p);
+    const char * p;
+    unsigned int xo;
+
+    for (xo = 0, p = s; *p; xo += 21, ++p)
+        setchar(x + xo, y, *p);
 }
 
 
 
 static void 
-setCG(int x,int y)
-{
-  int xo,yo,zo;
-
-  for(xo=0; xo<=50; xo++)
-  {
-    yo=sqrt(50.0*50.0-xo*xo);
-    setpixel(x+xo,y+yo,1);
-    setpixel(x+yo,y+xo,1);
-    setpixel(x-1-xo,y-1-yo,1);
-    setpixel(x-1-yo,y-1-xo,1);
-    setpixel(x+xo,y-1-yo,1);
-    setpixel(x-1-xo,y+yo,1);
-    for(zo=0; zo<yo; zo++)
-    {
-      setpixel(x+xo,y-1-zo,1);
-      setpixel(x-1-xo,y+zo,1);
+setCG(unsigned int const x,
+      unsigned int const y) {
+
+    unsigned int xo;
+
+    for (xo = 0; xo <= 50; ++xo)   {
+        unsigned int const yo = sqrt(SQR(50.0) - SQR(xo));
+
+        unsigned int zo;
+
+        setpixel(x + xo, y + yo, 1);
+        setpixel(x+yo,   y + xo, 1);
+        setpixel(x-1-xo, y-1-yo, 1);
+        setpixel(x-1-yo, y-1-xo, 1);
+        setpixel(x+xo,   y-1-yo, 1);
+        setpixel(x-1-xo, y+yo,   1);
+
+        for(zo = 0; zo < yo; ++zo) {
+            setpixel(x + xo, y-1-zo, 1);
+            setpixel(x-1-xo, y+zo,   1);
+        }
     }
-  }
 }
 
 
 
 static void
-outputPbm(FILE *        const file,
+outputPbm(FILE *        const ofP,
           struct bitmap const bitmap) {
 /*----------------------------------------------------------------------------
   Create a pbm file containing the image from the global variable bitmap[].
 -----------------------------------------------------------------------------*/
     int const forceplain = 0;
-    bit *pbmrow;
-    int row;
-    int bitmap_cursor;
+
+    unsigned int row;
     
-    pbm_writepbminit(file, bitmap.Width, bitmap.Height, forceplain);
-  
-    /* We round the allocated row space up to a multiple of 8 so the ugly
-       fast code below can work.
-       */
-    pbmrow = pbm_allocrow(((bitmap.Width+7)/8)*8);
+    pbm_writepbminit(ofP, bitmap.Width, bitmap.Height, forceplain);
     
-    bitmap_cursor = 0;
-    for (row = 0; row < bitmap.Height; row++) {
-        int col;
-        for (col = 0; col < bitmap.Width;) {
-            /* A little ugliness makes a big speed difference here. */
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<7);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<6);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<5);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<4);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<3);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<2);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<1);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<0);
-                
-            bitmap_cursor++;
-        }
-        pbm_writepbmrow(file, pbmrow, bitmap.Width, forceplain); 
+    for (row = 0; row < bitmap.Height; ++row) {
+        pbm_writepbmrow_packed(ofP, bitmap.bitmap[row],
+                               bitmap.Width, forceplain); 
     }
-    pbm_freerow(pbmrow);
-    pm_close(file);
 }
 
 
+
 static void
 framePerimeter(unsigned int const Width, 
                unsigned int const Height) {
 
-    unsigned int x,y;
+    unsigned int x, y;
 
     /* Top edge */
     for (x = 0; x < Width; ++x)
@@ -221,57 +217,66 @@ framePerimeter(unsigned int const Width,
 
 
 int 
-main(int argc,char** argv) {
+main(int argc, const char** argv) {
 
-    int TP=1;
-    int x,y;
+    int TP;
+    unsigned int x, y;
     char buf[128];
-    int Width;      /* width and height in 600ths of an inch */
-    int Height;
+    /* width and height in 600ths of an inch */
+    unsigned int Width;
+    unsigned int Height;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     if (argc > 1 && strcmp(argv[1], "-a4") == 0) {
-        Width = A4WIDTH;
+        Width  = A4WIDTH;
         Height = A4HEIGHT;
-        argc--;
-        argv++;
+        --argc;
+        ++argv;
     } else {
-        Width = USWIDTH;
+        Width  = USWIDTH;
         Height = USHEIGHT;
     }
 
-    bitmap.Width = Width;
+    if (argc > 1)
+        TP = atoi(argv[1]);
+    else
+        TP = 1;
+
+    bitmap.Width  = Width;
     bitmap.Height = Height;
-    bitmap.Pwidth = (Width + 7) / 8;
-    bitmap.bitmap = malloc(bitmap.Pwidth * bitmap.Height);
+    bitmap.bitmap = pbm_allocarray_packed(Width, bitmap.Height);
 
-    for (x = 0; x < bitmap.Pwidth * bitmap.Height; ++x)
-        bitmap.bitmap[x] = 0x00;
-    
-    if (argc>1)
-        TP = atoi(argv[1]);
+    for (y = 0; y < bitmap.Height; ++y) {
+        unsigned int x;
+        for (x = 0; x < pbm_packed_bytes(bitmap.Width); ++x) 
+            bitmap.bitmap[y][x] = 0x00; 
+    }
 
     switch (TP) {
     case 1:
         framePerimeter(Width, Height);
-        for (x = 0; x < Width; x += 100)
+        for (x = 0; x < Width; x += 100) {
+            unsigned int y;
             for(y = 0; y < Height; y += 100)
                 setplus(x, y, 4);
+        }
         for(x = 0; x < Width; x += 100) {
             sprintf(buf,"%d", x);
             setstring(x + 3, (Height/200) * 100 + 3, buf);
         }
-        for (y=0; y < Height; y += 100) {
+        for (y = 0; y < Height; y += 100) {
             sprintf(buf, "%d", y);
             setstring((Width/200) * 100 + 3, y + 3, buf);
         }
         for (x = 0; x < Width; x += 10)
             for (y = 0; y < Height; y += 100)
                 setplus(x, y, ((x%100) == 50) ? 2 : 1);
-        for (x=0; x < Width; x += 100)
+        for (x = 0; x < Width; x += 100) {
+            unsigned int y;
             for (y = 0; y < Height; y += 10)
                 setplus(x, y, ((y%100) == 50) ? 2 : 1);
+        }
         setCG(Width/2, Height/2);
         break;
     case 2:
@@ -290,5 +295,9 @@ main(int argc,char** argv) {
 
     outputPbm(stdout, bitmap);
 
+    pbm_freearray(bitmap.bitmap, Height);
+
+    pm_close(stdout);
+
     return 0;
 }
diff --git a/lib/libpam.c b/lib/libpam.c
index b890434e..9384e178 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -1066,6 +1066,67 @@ pnm_getopacity(const struct pam * const pamP,
 
 
 
+tuple
+pnm_backgroundtuple(struct pam *  const pamP,
+                    tuple      ** const tuples) {
+/*--------------------------------------------------------------------
+  This function was copied from libpnm3.c's pnm_backgroundxel() and
+  modified to use tuples instead of xels.
+----------------------------------------------------------------------*/
+    tuple tuplePtr, bgtuple, ul, ur, ll, lr;
+
+    /* Guess a good background value. */
+    ul = tuples[0][0];
+    ur = tuples[0][pamP->width-1];
+    ll = tuples[pamP->height-1][0];
+    lr = tuples[pamP->height-1][pamP->width-1];
+    bgtuple = NULL;
+
+    /* We first recognize three corners equal.  If not, we look for any
+       two.  If not, we just average all four.
+    */
+    if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, ll))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, ur) &&
+             pnm_tupleequal(pamP, ur, lr))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, ll) &&
+             pnm_tupleequal(pamP, ll, lr))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ur, ll) &&
+             pnm_tupleequal(pamP, ll, lr))
+        tuplePtr = ur;
+    else if (pnm_tupleequal(pamP, ul, ur))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, ll))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, lr))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ur, ll))
+        tuplePtr = ur;
+    else if (pnm_tupleequal(pamP, ur, lr))
+        tuplePtr = ur;
+    else if (pnm_tupleequal(pamP, ll, lr))
+        tuplePtr = ll;
+    else {
+        /* Reimplement libpnm3.c's mean4() but for tuples. */
+        unsigned int plane;
+        bgtuple = pnm_allocpamtuple(pamP);
+        for (plane = 0; plane < pamP->depth; ++plane)
+          bgtuple[plane] = (ul[plane] + ur[plane] + ll[plane] + lr[plane]) / 4;
+    }
+    if (!bgtuple) {
+        unsigned int plane;
+        bgtuple = pnm_allocpamtuple(pamP);
+        for (plane = 0; plane < pamP->depth; ++plane)
+          bgtuple[plane] = tuplePtr[plane];
+    }
+
+    return bgtuple;
+}
+
+
+
 /*=============================================================================
    pm_system() Standard Input feeder and Standard Output accepter functions.   
 =============================================================================*/
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
index ff75d0a1..793de6c5 100644
--- a/lib/libpbmfont.c
+++ b/lib/libpbmfont.c
@@ -1275,9 +1275,9 @@ skipCharacter(FILE * const fp) {
 
 
 static void
-validateEncoding(const char **  const arg,
-                 unsigned int * const codepointP,
-                 bool *         const badCodepointP) {
+interpEncoding(const char **  const arg,
+               unsigned int * const codepointP,
+               bool *         const badCodepointP) {
 /*----------------------------------------------------------------------------
    With arg[] being the ENCODING statement from the font, return as
    *codepointP the codepoint that it indicates (code point is the character
@@ -1315,12 +1315,29 @@ validateEncoding(const char **  const arg,
 
 
 
-
 static void
-processCharsLine(FILE *        const fp,
-                 const char ** const arg,
-                 struct font * const fontP) {
+readEncoding(FILE *         const ifP,
+             unsigned int * const codepointP,
+             bool *         const badCodepointP) {
+
+    const char * arg[32];
+
+    expect(ifP, "ENCODING", arg);
+    
+    interpEncoding(arg, codepointP, badCodepointP);
+}
 
+
+
+static void
+processChars(FILE *        const fp,
+             const char ** const arg,
+             struct font * const fontP) {
+/*----------------------------------------------------------------------------
+   Process the CHARS block in a BDF font file, assuming the file is positioned
+   just after the CHARS line and 'arg' is the contents of that CHARS line.
+   Read the rest of the block and apply its contents to *fontP.
+-----------------------------------------------------------------------------*/
     unsigned int const nCharacters = atoi(arg[1]);
 
     unsigned int nCharsDone;
@@ -1354,12 +1371,8 @@ processCharsLine(FILE *        const fp,
                 pm_error("no memory for font glyph for '%s' character",
                          charName);
 
-            {
-                const char * arg[32];
-                expect(fp, "ENCODING", arg);
+            readEncoding(fp, &codepoint, &badCodepoint);
 
-                validateEncoding(arg, &codepoint, &badCodepoint);
-            }
             if (badCodepoint)
                 skipCharacter(fp);
             else {
@@ -1389,7 +1402,7 @@ processCharsLine(FILE *        const fp,
                     const char * arg[32];
                     expect(fp, "ENDCHAR", arg);
                 }
-                assert(codepoint < 256); /* Ensured by validateEncoding() */
+                assert(codepoint < 256); /* Ensured by readEncoding() */
 
                 fontP->glyph[codepoint] = glyphP;
             }
@@ -1401,19 +1414,26 @@ processCharsLine(FILE *        const fp,
 
 
 static void
-processFontLine(FILE *        const fp,
-                const char *  const line,
-                const char ** const arg,
-                struct font * const fontP,
-                bool *        const endOfFontP) {
+processBdfFontLine(FILE *        const fp,
+                   const char *  const line,
+                   const char ** const arg,
+                   struct font * const fontP,
+                   bool *        const endOfFontP) {
+/*----------------------------------------------------------------------------
+   Process a nonblank line just read from a BDF font file.
 
+   This processing may involve reading more lines.
+-----------------------------------------------------------------------------*/
     *endOfFontP = FALSE;  /* initial assumption */
 
+    assert(arg[0] != NULL);  /* Entry condition */
+
     if (streq(arg[0], "COMMENT")) {
         /* ignore */
     } else if (streq(arg[0], "SIZE")) {
         /* ignore */
     } else if (streq(arg[0], "STARTPROPERTIES")) {
+        /* Read off the properties and ignore them all */
         unsigned int const propCount = atoi(arg[1]);
         unsigned int i;
         for (i = 0; i < propCount; ++i) {
@@ -1431,8 +1451,11 @@ processFontLine(FILE *        const fp,
         fontP->y         = atoi(arg[4]);
     } else if (streq(arg[0], "ENDFONT")) {
         *endOfFontP = true;
-    } else if (!strcmp(arg[0], "CHARS"))
-        processCharsLine(fp, arg, fontP);
+    } else if (streq(arg[0], "CHARS")) {
+        processChars(fp, arg, fontP);
+    } else {
+        /* ignore */
+    }
 }
 
 
@@ -1440,18 +1463,17 @@ processFontLine(FILE *        const fp,
 struct font *
 pbm_loadbdffont(const char * const name) {
 
-    FILE * fp;
-    char line[1024];
-    const char * arg[32];
+    FILE * ifP;
     struct font * fontP;
+    const char * wordList[32];
     bool endOfFont;
 
-    fp = fopen(name, "rb");
-    if (!fp)
+    ifP = fopen(name, "rb");
+    if (!ifP)
         pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
                  name, errno, strerror(errno));
 
-    expect(fp, "STARTFONT", arg);
+    expect(ifP, "STARTFONT", wordList);
 
     MALLOCVAR(fontP);
     if (fontP == NULL)
@@ -1471,12 +1493,14 @@ pbm_loadbdffont(const char * const name) {
     endOfFont = FALSE;
 
     while (!endOfFont) {
+        char line[1024];
+        const char * wordList[32];
         int rc;
-        rc = readline(fp, line, arg);
+        rc = readline(ifP, line, wordList);
         if (rc < 0)
             pm_error("End of file before ENDFONT statement in BDF font file");
 
-        processFontLine(fp, line, arg, fontP, &endOfFont);
+        processBdfFontLine(ifP, line, wordList, fontP, &endOfFont);
     }
     return fontP;
 }
diff --git a/lib/libpgm1.c b/lib/libpgm1.c
index 4d93e4be..64a43534 100644
--- a/lib/libpgm1.c
+++ b/lib/libpgm1.c
@@ -189,7 +189,7 @@ readRpgmRow(FILE * const fileP,
         asprintfN(&error, "Unable to allocate memory for row buffer "
                   "for %u columns", cols);
     else {
-        ssize_t rc;
+        size_t rc;
         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
         if (rc == 0)
             asprintfN(&error, "Error reading row.  fread() errno=%d (%s)",
diff --git a/lib/libpnm3.c b/lib/libpnm3.c
index c41a2108..0426ebcb 100644
--- a/lib/libpnm3.c
+++ b/lib/libpnm3.c
@@ -45,7 +45,7 @@ mean4(int const format,
         break;
 
     default:
-        pm_error( "Invalid format passed to pnm_backgroundxel()");
+        pm_error("Invalid format passed to pnm_backgroundxel()");
     }
     return retval;
 }
diff --git a/lib/libppmd.c b/lib/libppmd.c
index 3f824302..deb47c0f 100644
--- a/lib/libppmd.c
+++ b/lib/libppmd.c
@@ -966,14 +966,25 @@ typedef struct
     int edge;
 } coord;
 
-typedef struct fillobj {
+typedef struct fillState {
     int n;
+        /* Number of elements in 'coords' */
     int size;
     int curedge;
     int segstart;
     int ydir;
     int startydir;
     coord * coords;
+} fillState;
+
+typedef struct fillobj {
+
+    /* The only reason we have a struct fillState separate from
+       struct fillobj is that the drawproc interface is defined to
+       have drawing not modify the fillobj, i.e. it passed
+       const fillobj * to the drawing program.
+    */
+    struct fillState * stateP;
 } fillobj;
 
 #define SOME 1000
@@ -984,16 +995,24 @@ struct fillobj *
 ppmd_fill_create(void) {
 
     fillobj * fillObjP;
+    struct fillState * stateP;
 
     MALLOCVAR(fillObjP);
     if (fillObjP == NULL)
         pm_error("out of memory allocating a fillhandle");
-    fillObjP->n = 0;
-    fillObjP->size = SOME;
-    MALLOCARRAY(fillObjP->coords, fillObjP->size);
-    if (fillObjP->coords == NULL)
+
+    MALLOCVAR(stateP);
+    if (stateP == NULL)
+        pm_error("out of memory allocating a fillhandle");
+
+    stateP->n = 0;
+    stateP->size = SOME;
+    MALLOCARRAY(stateP->coords, stateP->size);
+    if (stateP->coords == NULL)
         pm_error("out of memory allocating a fillhandle");
-    fillObjP->curedge = 0;
+    stateP->curedge = 0;
+
+    fillObjP->stateP = stateP;
     
     /* Turn off line clipping. */
     /* UGGH! We must eliminate this global variable */
@@ -1021,14 +1040,95 @@ ppmd_fill_init(void) {
 
 
 void
-ppmd_fill_destroy(struct fillobj * fillObjP) {
+ppmd_fill_destroy(struct fillobj * const fillObjP) {
 
-    free(fillObjP->coords);
+    free(fillObjP->stateP->coords);
+    free(fillObjP->stateP);
     free(fillObjP);
 }
 
 
 
+static void
+addCoord(struct fillState *  const stateP,
+         ppmd_point const point) {
+
+    stateP->coords[stateP->n].point = point;
+    stateP->coords[stateP->n].edge = stateP->curedge;
+
+    ++stateP->n;
+}
+
+
+
+static void
+startNewSegment(struct fillState * const stateP) {
+/*----------------------------------------------------------------------------
+   Close off the segment we're currently building and start a new one.
+-----------------------------------------------------------------------------*/
+    if (stateP->startydir != 0 && stateP->ydir != 0) {
+        /* There's stuff in the current segment.  */
+        if (stateP->startydir == stateP->ydir) {
+            /* Oops, first edge and last edge of current segment are the same.
+               Change all points in the first edge to be in the last.
+            */
+            int const firstEdge = stateP->coords[stateP->segstart].edge;
+            int const lastEdge  = stateP->coords[stateP->n - 1].edge;
+            coord * const segStartCoordP = &stateP->coords[stateP->segstart];
+            coord * const segEndCoordP   = &stateP->coords[stateP->n];
+
+            coord * fcP;
+
+            for (fcP = segStartCoordP;
+                 fcP < segEndCoordP && fcP->edge == firstEdge;
+                 ++fcP)
+                fcP->edge = lastEdge;
+        }
+    }
+    /* And start new segment. */
+    ++stateP->curedge;
+    stateP->segstart  = stateP->n;
+    stateP->ydir      = 0;
+    stateP->startydir = 0;
+}
+
+
+
+static void
+continueSegment(struct fillState * const stateP,
+                int                const dy) {
+/*----------------------------------------------------------------------------
+   'dy' is how much the current point is above the previous one.
+-----------------------------------------------------------------------------*/
+    if (dy != 0) {
+        if (stateP->ydir != 0 && stateP->ydir != dy) {
+            /* Direction changed.  Insert a fake coord, old
+               position but new edge number.
+            */
+            ++stateP->curedge;
+            addCoord(stateP, stateP->coords[stateP->n - 1].point);
+        }
+        stateP->ydir = dy;
+        if (stateP->startydir == 0)
+            stateP->startydir = dy;
+    }
+}
+
+
+
+
+/* ppmd_fill_drawprocp() is a drawproc that turns an outline drawing function
+   into a filled shape function.  This is a somewhat off-label application of
+   a drawproc:  A drawproc is intended just to draw a point.  So e.g. you
+   might draw a circle with a fat brush by calling ppmd_circle with a drawproc
+   that draws a point as a 10-pixel disk.
+
+   But ppmd_fill_drawproc() just draws a point the trivial way: as one pixel.
+   However, it tracks every point that is drawn in a form that a subsequent
+   ppmd_fill() call can use to to fill in the shape drawn, assuming it turns
+   out to be a closed shape.
+*/
+
 void
 ppmd_fill_drawprocp(pixel **     const pixels, 
                     unsigned int const cols, 
@@ -1037,85 +1137,39 @@ ppmd_fill_drawprocp(pixel **     const pixels,
                     ppmd_point   const p,
                     const void * const clientdata) {
 
-    fillobj * fh;
-    coord * cp;
-
-    fh = (fillobj*) clientdata;
+    const fillobj *    const fillObjP = clientdata;
+    struct fillState * const stateP   = fillObjP->stateP;
 
-    /* If these are the same coords we saved last time, don't bother. */
-    if (fh->n > 0) {
-        ppmd_point const lastPoint = fh->coords[fh->n - 1].point;
-        if (pointsEqual(p, lastPoint))
-            return;
-    }
-
-    /* Ok, these are new; make room for two more coords. */
-    if (fh->n + 1 >= fh->size) {
-        fh->size += SOME;
-        REALLOCARRAY(fh->coords, fh->size);
-        if (fh->coords == NULL)
+    /* Make room for two more coords, the max we might add. */
+    if (stateP->n + 2 > stateP->size) {
+        stateP->size += SOME;
+        REALLOCARRAY(stateP->coords, stateP->size);
+        if (stateP->coords == NULL)
             pm_error("out of memory enlarging a fillhandle");
     }
 
-    /* Check for extremum and set the edge number. */
-    if (fh->n == 0) {
+    if (stateP->n == 0) {
         /* Start first segment. */
-        fh->segstart = fh->n;
-        fh->ydir = 0;
-        fh->startydir = 0;
+        stateP->segstart = stateP->n;
+        stateP->ydir = 0;
+        stateP->startydir = 0;
+        addCoord(stateP, p);
     } else {
-        coord * const ocp = &(fh->coords[fh->n - 1]);
-        int dx, dy;
-
-        dx = p.x - ocp->point.x;
-        dy = p.y - ocp->point.y;
-        if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
-            /* Segment break.  Close off old one. */
-            if (fh->startydir != 0 && fh->ydir != 0)
-                if (fh->startydir == fh->ydir) {
-                    /* Oops, first edge and last edge are the same.
-                       Renumber the first edge in the old segment.
-                    */
-                    const coord * const fcpLast= &(fh->coords[fh->n -1]); 
-                    coord * fcp;
-
-                    int oldedge;
-
-                    fcp = &(fh->coords[fh->segstart]);
-                    oldedge = fcp->edge;
-                    for (; fcp <= fcpLast && fcp->edge == oldedge ; ++fcp)
-                        fcp->edge = ocp->edge;
-                }
-            /* And start new segment. */
-            ++fh->curedge;
-            fh->segstart = fh->n;
-            fh->ydir = 0;
-            fh->startydir = 0;
+        ppmd_point const prevPoint = stateP->coords[stateP->n - 1].point;
+        int const dx = p.x - prevPoint.x;
+        int const dy = p.y - prevPoint.y;
+
+        if (dx == 0 && dy == 0) {
+            /* These are the same coords we had last time; don't bother */
         } else {
-            /* Segment continues. */
-            if (dy != 0) {
-                if (fh->ydir != 0 && fh->ydir != dy) {
-                    /* Direction changed.  Insert a fake coord, old
-                       position but new edge number.
-                    */
-                    ++fh->curedge;
-                    cp = &fh->coords[fh->n];
-                    cp->point = ocp->point;
-                    cp->edge = fh->curedge;
-                    ++fh->n;
-                }
-                fh->ydir = dy;
-                if (fh->startydir == 0)
-                    fh->startydir = dy;
-            }
+            if (abs(dx) > 1 || abs(dy) > 1)
+                startNewSegment(stateP);
+            else
+                continueSegment(stateP, dy);
+
+            addCoord(stateP, p);
         }
     }
-
-    /* Save this coord. */
-    cp = &fh->coords[fh->n];
-    cp->point = p;
-    cp->edge = fh->curedge;
-    ++fh->n;
 }
 
 
@@ -1173,10 +1227,12 @@ ppmd_fill(pixel **         const pixels,
           int              const cols, 
           int              const rows, 
           pixval           const maxval, 
-          struct fillobj * const fh,
+          struct fillobj * const fillObjP,
           ppmd_drawproc          drawProc,
           const void *     const clientdata) {
 
+    struct fillState * const fh = fillObjP->stateP;
+
     int pedge;
     int i, edge, lx, rx, py;
     coord * cp;
diff --git a/lib/pam.h b/lib/pam.h
index c28c5c2c..3d1f8532 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -483,6 +483,10 @@ pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
     ((tuple)[PAM_RED_PLANE] == (tuple)[PAM_GRN_PLANE] && \
      (tuple)[PAM_RED_PLANE] == (tuple)[PAM_BLU_PLANE])
 
+tuple
+pnm_backgroundtuple(struct pam *  const pamP,
+                    tuple      ** const tuples);
+
 /*----------------------------------------------------------------------------
    These are meant for passing to pm_system() as Standard Input feeder
    and Standard Output accepter.
diff --git a/lib/pnm.h b/lib/pnm.h
index e4bd34a8..de5f1d91 100644
--- a/lib/pnm.h
+++ b/lib/pnm.h
@@ -20,6 +20,9 @@ typedef pixval xelval;
 #define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL
 #define PNM_MAXMAXVAL PPM_MAXMAXVAL
 #define PNM_GET1(x) PPM_GETB(x)
+#define PNM_GETR(x) PPM_GETR(x)
+#define PNM_GETG(x) PPM_GETG(x)
+#define PNM_GETB(x) PPM_GETB(x)
 #define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,0,0,v)
 #define PNM_ASSIGN(x,r,g,b) PPM_ASSIGN(x,r,g,b)
 #define PNM_EQUAL(x,y) PPM_EQUAL(x,y)
diff --git a/lib/ppm.h b/lib/ppm.h
index 0695295f..57b6c04a 100644
--- a/lib/ppm.h
+++ b/lib/ppm.h
@@ -250,6 +250,12 @@ ppm_hsv_from_color(pixel  const color,
                    pixval const maxval);
 
 static __inline pixval
+ppm_luminosity(pixel const p) {
+
+    return (pixval)(PPM_LUMIN(p) + 0.5);
+}
+
+static __inline pixval
 ppm_colorvalue(pixel const p) {
 /*----------------------------------------------------------------------------
   The color value (V is HSV) as a pixval
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
index 2175fc5d..e5e138e1 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -1,4 +1,5 @@
-/*=============================================================================
+#if 0
+=============================================================================
 HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 
 
@@ -6,8 +7,9 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 int 
 main ( int argc, char **argv ) {
 
-    // initial values here are just to demonstrate what gets set and
-    // what doesn't by the code below.
+    /* initial values here are just to demonstrate what gets set and
+       what doesn't by the code below.
+    */
     int help_flag = 7;
     unsigned int help_spec =7;
     unsigned int height_spec =7;
@@ -67,8 +69,8 @@ Now run this program with something like
   myprog -v /etc/passwd -name=Bryan --hei=4
 
 
-========================================================================*/
-
+========================================================================
+#endif /* 0 */
 
 #ifndef SHHOPT_H
 #define SHHOPT_H
diff --git a/netpbm.c b/netpbm.c
index c47cb37e..7f3e7c96 100644
--- a/netpbm.c
+++ b/netpbm.c
@@ -29,13 +29,17 @@
 int
 main(int argc, char *argv[]) {
 
-    char* cp;
+    const char * cp;
     
     if (strcmp(pm_arg0toprogname(argv[0]), "netpbm") == 0) {
         ++argv;
         --argc;
         if (argc < 1 || !*argv)	{
-            fprintf(stderr, "Usage: netpbm netpbm_program_name [args ...]\n");
+            fprintf(stderr,
+                    "When you invoke this program by the name 'netpbm', "
+                    "You must supply at least one argument: the name of "
+                    "the Netpbm program to run, e.g. "
+                    "'netpbm pamfile /tmp/myfile.ppm'\n");
             exit(1);
 		}
 	}
diff --git a/version.mk b/version.mk
index 4cb726b4..ebeabbf3 100644
--- a/version.mk
+++ b/version.mk
@@ -1,4 +1,4 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 49
-NETPBM_POINT_RELEASE = 5
+NETPBM_MINOR_RELEASE = 50
+NETPBM_POINT_RELEASE =  0