about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-04-28 01:41:04 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-04-28 01:41:04 +0000
commit57b3c8cbeca6b911ad989933f391ef36ea1b4a64 (patch)
tree9e9092a064ebe8917a77068b1b0f878fe950073f
parentab14226a9e1cf07b26a708aacf1dc6384fc23835 (diff)
downloadnetpbm-mirror-57b3c8cbeca6b911ad989933f391ef36ea1b4a64.tar.gz
netpbm-mirror-57b3c8cbeca6b911ad989933f391ef36ea1b4a64.tar.xz
netpbm-mirror-57b3c8cbeca6b911ad989933f391ef36ea1b4a64.zip
Release 10.46.03
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@901 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--converter/pbm/pbmtomacp.c3
-rw-r--r--converter/ppm/ximtoppm.c4
-rw-r--r--converter/ppm/xvminitoppm.c8
-rw-r--r--doc/HISTORY26
-rw-r--r--editor/pnmremap.c506
-rw-r--r--editor/pnmtile.c2
-rwxr-xr-xeditor/ppmfade2
-rwxr-xr-xeditor/ppmquantall4
-rw-r--r--generator/ppmpat.c2
-rwxr-xr-xgenerator/ppmrainbow20
-rw-r--r--lib/ppmdfont.c12
-rw-r--r--version.mk2
12 files changed, 447 insertions, 144 deletions
diff --git a/converter/pbm/pbmtomacp.c b/converter/pbm/pbmtomacp.c
index 82b55904..ad0b22b1 100644
--- a/converter/pbm/pbmtomacp.c
+++ b/converter/pbm/pbmtomacp.c
@@ -38,7 +38,6 @@ char *argv[];
   int argn, rows, cols;
   int left,bottom,right,top;
   int lflg, rflg, tflg, bflg;
-  char name[100];
   const char * const usage = "[-l left] [-r right] [-b bottom] [-t top] [pbmfile]";
 
 
@@ -84,11 +83,9 @@ char *argv[];
 
   if ( argn == argc )
   { ifp = stdin;
-    strcpy( name, "noname" );
   }
   else
   { ifp = pm_openr( argv[argn] );
-    strcpy( name, argv[argn] );
     ++argn;
   }
 
diff --git a/converter/ppm/ximtoppm.c b/converter/ppm/ximtoppm.c
index 98aad2d7..15589c16 100644
--- a/converter/ppm/ximtoppm.c
+++ b/converter/ppm/ximtoppm.c
@@ -52,6 +52,10 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0,   "alphaout",   OPT_STRING, 
             &cmdlineP->alpha_filename, &alphaoutSpec, 0);
 
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
     optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and all of *cmdlineP. */
 
diff --git a/converter/ppm/xvminitoppm.c b/converter/ppm/xvminitoppm.c
index 16626bc2..d76bea87 100644
--- a/converter/ppm/xvminitoppm.c
+++ b/converter/ppm/xvminitoppm.c
@@ -53,7 +53,7 @@ parseCommandLine(int const argc,
 
 
 static void
-getline(FILE * const ifP,
+getLine(FILE * const ifP,
         char * const buf,
         size_t const size) {
 
@@ -107,7 +107,7 @@ readXvHeader(FILE *         const ifP,
     int rc;
     bool endOfComments;
     
-    getline(ifP, buf, sizeof(buf));
+    getLine(ifP, buf, sizeof(buf));
 
     if (!strneq(buf, "P7 332", 6))
         pm_error("Input is not a XV thumbnail picture.  It does not "
@@ -115,14 +115,14 @@ readXvHeader(FILE *         const ifP,
 
     endOfComments = FALSE;
     while (!endOfComments) {
-        getline(ifP, buf, sizeof(buf));
+        getLine(ifP, buf, sizeof(buf));
         if (strneq(buf, "#END_OF_COMMENTS", 16))
             endOfComments = TRUE;
         else if (strneq(buf, "#BUILTIN", 8))
             pm_error("This program does not know how to "
                      "convert builtin XV thumbnail pictures");
     }
-    getline(ifP, buf, sizeof(buf));
+    getLine(ifP, buf, sizeof(buf));
     rc = sscanf(buf, "%u %u %u", &cols, &rows, &maxval);
     if (rc != 3)
         pm_error("error parsing dimension info '%s'.  "
diff --git a/doc/HISTORY b/doc/HISTORY
index 36a446ab..e982d2bf 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,6 +4,32 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
+09.04.28 BJH  Release 10.46.03
+
+              pnmremap: fix loss of dithering when the map (and thus the
+              output) has lower maxval than the input.  (Old code scales down
+              to the new maxval before dithering).
+
+              ximtoppm: fix crash in command line processing.
+
+              ppmrainbow: exit with proper exit status upon failure.
+              
+              ppmfade: eliminate reference to undeclared subroutine.
+
+              ppmquantall: don't use 'set' to set Bourne shell variable.
+
+              pbmtomacp: fix buffer overflow.
+
+              ppmpat: fix usage statement.
+
+              pnmtile: fix reference to arbitrary storage in option
+              processing.  Introduced in 10.42.
+
+              libnetpbm (ppmd): fix: don't ignore failure to read font file.
+
+              build: rename getline() in xvminitoppm.c to avoid collision
+              with libc.
+
 09.04.12 BJH  Release 10.46.02
 
               pamstereogram: fix tuple type in output file (and crash
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index 209124a1..7301fa35 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -40,15 +40,6 @@ enum missingMethod {
     MISSING_CLOSE
 };
 
-#define FS_SCALE 1024
-
-struct fserr {
-    long ** thiserr;
-    long ** nexterr;
-    bool    fsForward;
-};
-
-
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -146,53 +137,112 @@ parseCommandLine (int argc, char ** argv,
 }
 
 
+typedef enum {
+    ADJUST_NONE,
+    ADJUST_RGBTO1,
+    ADJUST_GRAYSCALETO3
+} depthAdjustment;
+
+
 
 static void
-rgbToDepth1(const struct pam * const pamP,
-            tuple *            const tupleRow) {
-    
-    unsigned int col;
+rgbToDepth1(tuple const tuple) {
 
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        double grayvalue;
-        grayvalue = 0.0;  /* initial value */
-        for (plane = 0; plane < pamP->depth; ++plane)
-            grayvalue += pnm_lumin_factor[plane] * tupleRow[col][plane];
-        tupleRow[col][0] = (sample) (grayvalue + 0.5);
-    }
+    unsigned int plane;
+    double grayvalue;
+
+    grayvalue = 0.0;  /* initial value */
+
+    for (plane = 0; plane < 3; ++plane)
+        grayvalue += pnm_lumin_factor[plane] * tuple[plane];
+
+    tuple[0] = (sample) (grayvalue + 0.5);
 }
 
 
 
 static void
-grayscaleToDepth3(const struct pam * const pamP,
-                  tuple *            const tupleRow) {
+grayscaleToDepth3(tuple const tuple) {
+
+    tuple[1] = tuple[0];
+    tuple[2] = tuple[0];
+}
+
+
+
+static void
+adjustDepthTuple(tuple           const tuple,
+                 depthAdjustment const adjustment) {
     
-    unsigned int col;
+    switch (adjustment) {
+    case ADJUST_NONE:
+        break;
+    case ADJUST_RGBTO1:
+        rgbToDepth1(tuple);
+        break;
+    case ADJUST_GRAYSCALETO3:
+        grayscaleToDepth3(tuple);
+        break;
+    }
+}
+
 
-    assert(pamP->allocation_depth >= 3);
 
-    for (col = 0; col < pamP->width; ++col) {
-        tupleRow[col][1] = tupleRow[col][0];
-        tupleRow[col][2] = tupleRow[col][0];
+static void
+inverseAdjustDepthTuple(tuple           const tuple,
+                        depthAdjustment const adjustment) {
+    
+    switch (adjustment) {
+    case ADJUST_NONE:
+        break;
+    case ADJUST_RGBTO1:
+        grayscaleToDepth3(tuple);
+        break;
+    case ADJUST_GRAYSCALETO3:
+        rgbToDepth1(tuple);
+        break;
     }
 }
 
 
 
 static void
-adjustDepth(const struct pam * const pamP,
-            tuple *            const tupleRow,
-            unsigned int       const newDepth) {
+adjustDepthRow(tuple *         const tupleRow,
+               unsigned int    const width,
+               depthAdjustment const adjustment) {
 /*----------------------------------------------------------------------------
-   Change the depth of the raster row tupleRow[] of the image
-   described by 'pamP' to newDepth.
+   Change tupleRow[] depth as indicated by 'adjustment',
+   i.e. turned from RGB to grayscale or grayscale to RGB.
 
-   We don't change the memory allocation; tupleRow[] must already have
-   space allocated for at least 'newDepth' planes.  When we're done,
-   all but the first 'newDepth' planes are meaningless, but the space is
-   still there.
+   We assume tupleRow[] is consistent with 'adjustment' -- i.e. if
+   'adjustment' says grayscale to RGB, tupleRow[] has an allocation depth of
+   at least 3 and if 'adjustment' says from RGB to grayscale, tupleRow[] has
+   RGB tuples.
+-----------------------------------------------------------------------------*/
+    if (adjustment == ADJUST_NONE) {
+    } else {
+        unsigned int col;
+
+        for (col = 0; col < width; ++col) {
+            if (adjustment == ADJUST_RGBTO1)
+                rgbToDepth1(tupleRow[col]);
+            else {
+                assert(adjustment == ADJUST_GRAYSCALETO3);
+                grayscaleToDepth3(tupleRow[col]);
+            }
+        }
+    }
+}
+
+
+
+static void
+selectDepthAdjustment(const struct pam * const pamP,
+                      unsigned int       const newDepth,
+                      depthAdjustment *  const adjustmentP) {
+/*----------------------------------------------------------------------------
+   Determine what kind of depth adjustment the pixels of an image described
+   by 'pamP' need to be comparable to pixels with depth 'newDepth'.
 
    The only depth changes we know how to do are:
 
@@ -200,15 +250,23 @@ adjustDepth(const struct pam * const pamP,
 
        We change it to grayscale or black and white.
 
+       For this, we return *adjustmentP == ADJUST_RGBTO1.
+
      - from tuple type GRAYSCALE or BLACKANDWHITE depth 1 to depth 3.
 
        We change it to RGB.
 
+       For this, we return *adjustmentP == ADJUST_GRAYSCALETO3.
+
    For any other depth change request, we issue an error message and abort
    the program.
------------------------------------------------------------------------------*/
-    if (newDepth != pamP->depth) {
 
+   If 'newDepth' is the same depth as the original (no depth change required),
+   we return *adjustmentP == ADJUST_NONE.
+-----------------------------------------------------------------------------*/
+    if (newDepth == pamP->depth)
+        *adjustmentP = ADJUST_NONE;
+    else {
         if (stripeq(pamP->tuple_type, "RGB")) {
             if (newDepth != 1) {
                 pm_error("Map image depth of %u differs from input image "
@@ -217,7 +275,7 @@ adjustDepth(const struct pam * const pamP,
                          "an RGB tuple is 1.",
                          newDepth, pamP->depth);
             } else
-                rgbToDepth1(pamP, tupleRow);
+                *adjustmentP = ADJUST_RGBTO1;
         } else if (stripeq(pamP->tuple_type, "GRAYSCALE") ||
                    stripeq(pamP->tuple_type, "BLACKANDWHITE")) {
             if (newDepth != 3) {
@@ -228,7 +286,7 @@ adjustDepth(const struct pam * const pamP,
                          "a GRAYSCALE or BLACKANDWHITE tuple is 3.",
                          newDepth, pamP->depth);
             } else
-                grayscaleToDepth3(pamP, tupleRow);
+                *adjustmentP = ADJUST_GRAYSCALETO3;
         } else {
             pm_error("Map image depth of %u differs from input image depth "
                      "of %u, and the input image does not have a tuple type "
@@ -243,7 +301,6 @@ adjustDepth(const struct pam * const pamP,
 
 
 
-
 static void
 computeColorMapFromMap(struct pam *   const mappamP, 
                        tuple **       const maptuples, 
@@ -266,12 +323,38 @@ computeColorMapFromMap(struct pam *   const mappamP,
         pnm_computetuplefreqtable(mappamP, maptuples, MAXCOLORS, &colors);
     if (*colormapP == NULL)
         pm_error("too many colors in colormap!");
-    pm_message("%d colors found in colormap", colors);
+    pm_message("%u colors found in colormap", colors);
     *newcolorsP = colors;
 }
 
 
 
+#define FS_SCALE 1024
+
+struct fserr {
+    unsigned int width;
+        /* Width of the image being dithered */
+    long ** thiserr;
+    long ** nexterr;
+    bool    fsForward;
+        /* We are in a left-to-right row */
+    int     begCol;
+        /* First column in the sweep.  Determined by 'fsForward': either
+           the leftmost or rightmost column in the row
+        */
+    int     endCol;
+        /* Column after the last column in the sweep.  Determined by
+           'fsForward': either one past the left end or one past the right
+           end of the row.
+        */
+    int     step;
+        /* What we add to a column number to get the next one in the sweep.
+           Determined by 'fsForward': +1 or -1.
+        */
+};
+
+
+
 static void
 randomizeError(long **      const err,
                unsigned int const width,
@@ -294,6 +377,28 @@ randomizeError(long **      const err,
 
 
 static void
+fserrSetForward(struct fserr * const fserrP) {
+
+    fserrP->fsForward = TRUE;
+    fserrP->begCol = 0;
+    fserrP->endCol = fserrP->width;
+    fserrP->step   = +1;
+}
+
+
+
+static void
+fserrSetBackward(struct fserr * const fserrP) {
+
+    fserrP->fsForward = FALSE;
+    fserrP->begCol = fserrP->width - 1;
+    fserrP->endCol = -1;
+    fserrP->step   = -1;
+}
+
+
+
+static void
 initFserr(struct pam *   const pamP,
           struct fserr * const fserrP,
           bool           const initRandom) {
@@ -304,6 +409,8 @@ initFserr(struct pam *   const pamP,
 
     unsigned int const fserrSize = pamP->width + 2;
 
+    fserrP->width = pamP->width;
+
     MALLOCARRAY(fserrP->thiserr, pamP->depth);
     if (fserrP->thiserr == NULL)
         pm_error("Out of memory allocating Floyd-Steinberg structures "
@@ -327,7 +434,7 @@ initFserr(struct pam *   const pamP,
     if (initRandom)
         randomizeError(fserrP->thiserr, fserrSize, pamP->depth);
 
-    fserrP->fsForward = TRUE;
+    fserrSetForward(fserrP);
 }
 
 
@@ -348,7 +455,8 @@ floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
 
 static void
 floydAdjustColor(struct pam *   const pamP, 
-                 tuple          const tuple, 
+                 tuple          const intuple, 
+                 tuple          const outtuple, 
                  struct fserr * const fserrP, 
                  int            const col) {
 /*----------------------------------------------------------------------------
@@ -358,8 +466,8 @@ floydAdjustColor(struct pam *   const pamP,
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         long int const s =
-            tuple[plane] + fserrP->thiserr[plane][col+1] / FS_SCALE;
-        tuple[plane] = MIN(pamP->maxval, MAX(0,s));
+            intuple[plane] + fserrP->thiserr[plane][col+1] / FS_SCALE;
+        outtuple[plane] = MIN(pamP->maxval, MAX(0,s));
     }
 }
 
@@ -411,7 +519,11 @@ floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
         fserrP->thiserr[plane] = fserrP->nexterr[plane];
         fserrP->nexterr[plane] = temperr;
     }
-    fserrP->fsForward = ! fserrP->fsForward;
+
+    if (fserrP->fsForward)
+        fserrSetBackward(fserrP);
+    else
+        fserrSetForward(fserrP);
 }
 
 
@@ -631,8 +743,177 @@ lookupThroughHash(struct pam *            const pamP,
 
 
 static void
-convertRow(struct pam *            const pamP, 
-           tuple                         tuplerow[],
+mapTuple(struct pam *            const pamP,
+         tuple                   const inTuple,
+         enum missingMethod      const missingMethod, 
+         tuple                   const defaultColor,
+         tupletable              const colormap,
+         struct colormapFinder * const colorFinderP,
+         tuplehash               const colorhash, 
+         bool *                  const usehashP,
+         tuple                   const outTuple,
+         bool *                  const missingP) {
+
+    int colormapIndex;
+        /* Index into the colormap of the replacement color, or -1 if
+           there is no usable color in the color map.
+        */
+
+    lookupThroughHash(pamP, inTuple, 
+                      missingMethod != MISSING_CLOSE, colorFinderP, 
+                      colorhash, &colormapIndex, usehashP);
+
+    if (colormapIndex == -1) {
+        *missingP = TRUE;
+        switch (missingMethod) {
+        case MISSING_SPECIFIED:
+            pnm_assigntuple(pamP, outTuple, defaultColor);
+            break;
+        case MISSING_FIRST:
+            pnm_assigntuple(pamP, outTuple, colormap[0]->tuple);
+            break;
+        default:
+            pm_error("Internal error: invalid value of missingMethod");
+        }
+    } else {
+        *missingP = FALSE;
+        pnm_assigntuple(pamP, outTuple, colormap[colormapIndex]->tuple);
+    }
+}
+
+
+
+static void
+convertRowStraight(struct pam *            const inpamP,
+                   struct pam *            const outpamP, 
+                   tuple                         inrow[],
+                   depthAdjustment         const depthAdjustment,
+                   tupletable              const colormap,
+                   struct colormapFinder * const colorFinderP,
+                   tuplehash               const colorhash, 
+                   bool *                  const usehashP,
+                   enum missingMethod      const missingMethod, 
+                   tuple                   const defaultColor,
+                   tuple                         outrow[],
+                   unsigned int *          const missingCountP) {
+/*----------------------------------------------------------------------------
+  Like convertRow(), compute outrow[] from inrow[], replacing each pixel with
+  the new colors.  Do a straight pixel for pixel remap; no dithering.
+
+  Return the number of pixels that were not matched in the color map as
+  *missingCountP.
+
+  *colorFinderP is a color finder based on 'colormap' -- it tells us what
+  index of 'colormap' corresponds to a certain color.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int missingCount;
+    
+    /* The following modify tuplerow, to make it consistent with
+     *outpamP instead of *inpamP.
+     */
+    assert(outpamP->allocation_depth >= inpamP->depth);
+
+    pnm_scaletuplerow(inpamP, outrow, inrow, outpamP->maxval);
+
+    adjustDepthRow(outrow, outpamP->width, depthAdjustment);
+
+    missingCount = 0;  /* initial value */
+    
+    for (col = 0; col < outpamP->width; ++col) {
+        bool missing;
+        mapTuple(outpamP, outrow[col], missingMethod, defaultColor,
+                 colormap, colorFinderP,
+                 colorhash, usehashP, outrow[col], &missing);
+
+        if (missing)
+            ++missingCount;
+    }
+
+    *missingCountP = missingCount;
+}
+
+
+
+static void
+convertRowDither(struct pam *            const inpamP,
+                 struct pam *            const outpamP,
+                 tuple                   const inrow[],
+                 depthAdjustment         const depthAdjustment,
+                 tupletable              const colormap,
+                 struct colormapFinder * const colorFinderP,
+                 tuplehash               const colorhash, 
+                 bool *                  const usehashP,
+                 enum missingMethod      const missingMethod, 
+                 tuple                   const defaultColor,
+                 struct fserr *          const fserrP,
+                 tuple                         outrow[],
+                 unsigned int *          const missingCountP) {
+/*----------------------------------------------------------------------------
+  Like convertRow(), compute outrow[] from inrow[], replacing each pixel with
+  the new colors.  Do a Floyd-Steinberg dither, using and updating the error
+  accumulator *fserrP.
+
+  Return the number of pixels that were not matched in the color map as
+  *missingCountP.
+
+  *colorFinderP is a color finder based on 'colormap' -- it tells us what
+  index of 'colormap' corresponds to a certain color.
+-----------------------------------------------------------------------------*/
+    tuple const ditheredTuple = pnm_allocpamtuple(inpamP);
+        /* The input tuple we're converting, adjusted by the dither */
+    tuple const normTuple = pnm_allocpamtuple(outpamP);
+        /* Same as above, normalized to the maxval of the output file /
+           colormap.
+        */
+    unsigned int missingCount;
+    int col;
+
+    floydInitRow(inpamP, fserrP);
+
+    missingCount = 0;  /* initial value */
+    
+    for (col = fserrP->begCol; col != fserrP->endCol; col += fserrP->step) {
+        bool missing;
+
+        floydAdjustColor(inpamP, inrow[col], ditheredTuple, fserrP, col);
+
+        /* Convert tuple to the form of those in the colormap */
+        assert(outpamP->allocation_depth >= inpamP->depth);
+        pnm_scaletuple(inpamP, normTuple, ditheredTuple, outpamP->maxval);
+        adjustDepthTuple(normTuple, depthAdjustment);
+
+        mapTuple(outpamP, normTuple, missingMethod, defaultColor,
+                 colormap, colorFinderP,
+                 colorhash, usehashP, outrow[col], &missing);
+
+        if (missing)
+            ++missingCount;
+
+        /* Convert tuple back to the form of the input, where dithering
+           takes place.
+        */
+        pnm_scaletuple(outpamP, normTuple, outrow[col], inpamP->maxval);
+        inverseAdjustDepthTuple(normTuple, depthAdjustment);
+
+        floydPropagateErr(inpamP, fserrP, col, inrow[col], normTuple);
+    }
+
+    floydSwitchDir(inpamP, fserrP);
+
+    pnm_freepamtuple(normTuple);
+    pnm_freepamtuple(ditheredTuple);
+
+    *missingCountP = missingCount;
+}
+
+
+
+static void
+convertRow(struct pam *            const inpamP,
+           struct pam *            const outpamP,
+           tuple                         inrow[],
+           depthAdjustment               depthAdjustment,
            tupletable              const colormap,
            struct colormapFinder * const colorFinderP,
            tuplehash               const colorhash, 
@@ -641,10 +922,11 @@ convertRow(struct pam *            const pamP,
            enum missingMethod      const missingMethod, 
            tuple                   const defaultColor,
            struct fserr *          const fserrP,
+           tuple                         outrow[],
            unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
-  Replace the colors in row tuplerow[] (described by *pamP) with the
-  new colors.
+  Replace the colors in row tuplerow[] (described by *inpamP) with the
+  new colors and convert so it is described by *outpamP.
 
   Use and update the Floyd-Steinberg state *fserrP.
 
@@ -653,66 +935,25 @@ convertRow(struct pam *            const pamP,
 
   *colorFinderP is a color finder based on 'colormap' -- it tells us what
   index of 'colormap' corresponds to a certain color.
------------------------------------------------------------------------------*/
-    int col;
-    int limitcol;
-        /* The column at which to stop processing the row.  If we're scanning
-           forwards, this is the rightmost column.  If we're scanning 
-           backward, this is the leftmost column.
-        */
-    
-    if (floyd)
-        floydInitRow(pamP, fserrP);
-
-    *missingCountP = 0;  /* initial value */
-    
-    if ((!floyd) || fserrP->fsForward) {
-        col = 0;
-        limitcol = pamP->width;
-    } else {
-        col = pamP->width - 1;
-        limitcol = -1;
-    }
-    do {
-        int colormapIndex;
-            /* Index into the colormap of the replacement color, or -1 if
-               there is no usable color in the color map.
-            */
-
-        if (floyd) 
-            floydAdjustColor(pamP, tuplerow[col], fserrP, col);
-
-        lookupThroughHash(pamP, tuplerow[col], 
-                          missingMethod != MISSING_CLOSE, colorFinderP, 
-                          colorhash, &colormapIndex, usehashP);
-        if (floyd)
-            floydPropagateErr(pamP, fserrP, col, tuplerow[col], 
-                              colormap[colormapIndex]->tuple);
-
-        if (colormapIndex == -1) {
-            ++*missingCountP;
-            switch (missingMethod) {
-            case MISSING_SPECIFIED:
-                pnm_assigntuple(pamP, tuplerow[col], defaultColor);
-                break;
-            case MISSING_FIRST:
-                pnm_assigntuple(pamP, tuplerow[col], colormap[0]->tuple);
-                break;
-            default:
-                pm_error("Internal error: invalid value of missingMethod");
-            }
-        } else 
-            pnm_assigntuple(pamP, tuplerow[col], 
-                            colormap[colormapIndex]->tuple);
-
-        if (floyd && !fserrP->fsForward) 
-            --col;
-        else
-            ++col;
-    } while (col != limitcol);
 
+  outrow[] doubles as a work space, so we require it to have an allocation
+  depth at least as great as that of inrow[].
+-----------------------------------------------------------------------------*/
+    /* The following both consults and adds to 'colorhash' and
+       its associated '*usehashP'.  It modifies 'tuplerow' too.
+    */
     if (floyd)
-        floydSwitchDir(pamP, fserrP);
+        convertRowDither(inpamP, outpamP, inrow,
+                         depthAdjustment, colormap, colorFinderP, colorhash,
+                         usehashP,
+                         missingMethod, defaultColor,
+                         fserrP, outrow, missingCountP);
+    else 
+        convertRowStraight(inpamP, outpamP, inrow,
+                           depthAdjustment, colormap, colorFinderP, colorhash,
+                           usehashP,
+                           missingMethod, defaultColor,
+                           outrow, missingCountP);
 }
 
 
@@ -729,12 +970,30 @@ copyRaster(struct pam *       const inpamP,
            unsigned int *     const missingCountP) {
 
     tuplehash const colorhash = pnm_createtuplehash();
+
+    tuple * inrow;
+    tuple * outrow;
+    struct pam workpam;
+        /* This is for work space we use for building up the output
+           pixels.  To save time and memory, we modify them in place in a
+           buffer, which ultimately holds the output pixels.  This pam
+           structure is thus the same as the *outpamP, but with a tuple
+           allocation depth large enough to handle intermediate results.
+        */
+    depthAdjustment depthAdjustment;
     struct colormapFinder * colorFinderP;
     bool usehash;
     struct fserr fserr;
-    tuple * tuplerow = pnm_allocpamrow(inpamP);
     int row;
 
+    workpam = *outpamP;
+    workpam.allocation_depth = MAX(workpam.depth, inpamP->depth);
+    workpam.size             = sizeof(workpam);
+    workpam.len              = PAM_STRUCT_SIZE(allocation_depth);
+
+    inrow  = pnm_allocpamrow(inpamP);
+    outrow = pnm_allocpamrow(&workpam);
+
     if (outpamP->maxval != inpamP->maxval && missingMethod != MISSING_CLOSE)
         pm_error("The maxval of the colormap (%u) is not equal to the "
                  "maxval of the input image (%u).  This is allowable only "
@@ -742,6 +1001,8 @@ copyRaster(struct pam *       const inpamP,
                  "specify -firstisdefault or -missingcolor)",
                  (unsigned int)outpamP->maxval, (unsigned int)inpamP->maxval);
 
+    selectDepthAdjustment(inpamP, outpamP->depth, &depthAdjustment);
+
     usehash = TRUE;
 
     createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP);
@@ -754,28 +1015,21 @@ copyRaster(struct pam *       const inpamP,
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int missingCount;
 
-        pnm_readpamrow(inpamP, tuplerow);
+        pnm_readpamrow(inpamP, inrow);
 
-        /* The following modify tuplerow, to make it consistent with
-           *outpamP instead of *inpamP.
-        */
-        assert(inpamP->allocation_depth >= outpamP->depth);
-        pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval);
-        adjustDepth(inpamP, tuplerow, outpamP->depth); 
-
-        /* The following both consults and adds to 'colorhash' and
-           its associated 'usehash'.  It modifies 'tuplerow' too.
-        */
-        convertRow(outpamP, tuplerow, colormap, colorFinderP, colorhash, 
+        convertRow(inpamP, &workpam, inrow,
+                   depthAdjustment, colormap, colorFinderP, colorhash,
                    &usehash,
-                   floyd, missingMethod, defaultColor, &fserr, &missingCount);
+                   floyd, missingMethod, defaultColor,
+                   &fserr,  outrow, &missingCount);
         
         *missingCountP += missingCount;
         
-        pnm_writepamrow(outpamP, tuplerow);
+        pnm_writepamrow(outpamP, outrow);
     }
     destroyColormapFinder(colorFinderP);
-    pnm_freepamrow(tuplerow);
+    pnm_freepamrow(inrow);
+    pnm_freepamrow(outrow);
     pnm_destroytuplehash(colorhash);
 }
 
diff --git a/editor/pnmtile.c b/editor/pnmtile.c
index 26728055..21512b36 100644
--- a/editor/pnmtile.c
+++ b/editor/pnmtile.c
@@ -50,6 +50,8 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
+    OPTENTINIT;
+
     optParseOptions3(&argc, (char**)argv, opt, sizeof opt, 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
diff --git a/editor/ppmfade b/editor/ppmfade
index 2507eaf2..9bd122e9 100755
--- a/editor/ppmfade
+++ b/editor/ppmfade
@@ -35,6 +35,8 @@ my $mode = $SPREAD;		# default fading mode
 #
 #  Check those command line args.
 #
+sub usage();
+
 if (@ARGV == 0) {
     usage();
 }
diff --git a/editor/ppmquantall b/editor/ppmquantall
index 7f3d4849..4807de8c 100755
--- a/editor/ppmquantall
+++ b/editor/ppmquantall
@@ -62,8 +62,8 @@ files=($@)
 # To be robust, we need to use Pnmfile to get that information, or 
 # Put this program in C and use ppm_readppminit().
 
-set widths=()
-set heights=()
+widths=()
+heights=()
 
 for i in ${files[@]}; do
     widths=(${widths[*]} `grep -v '^#' $i | sed '1d; s/ .*//; 2q'`)
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 6034b2db..c07b298c 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -867,7 +867,7 @@ main(int argc, char ** argv) {
 #define PAT_CAMO 7
 #define PAT_ANTICAMO 8
 #define PAT_TEST 9
-    const char* const usage = "-gingham|-g2|-gingham3|-g3|-madras|-tartan|-poles|-squig|-camo|-anticamo <width> <height>";
+    const char* const usage = "-gingham2|-g2|-gingham3|-g3|-madras|-tartan|-poles|-squig|-camo|-anticamo <width> <height>";
 
 
     ppm_init(&argc, argv);
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index 0effeecf..f98536cd 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -6,6 +6,13 @@ my ($FALSE, $TRUE) = (0,1);
 
 (my $myname = $0) =~ s#\A.*/##;
 
+sub fatal($) {
+    my ($msg) = @_;
+
+    print(STDERR "$msg\n");
+    exit(1);
+}
+
 my ($Twid, $Thgt, $tmpdir, $norepeat, $verbose);
 
 # set defaults
@@ -21,15 +28,16 @@ GetOptions("width=i"   => \$Twid,
            "norepeat!" => \$norepeat,
            "verbose!"  => \$verbose);
 
-die "invalid width and/or height\n" unless $Twid >= 1 && $Thgt >= 1;
-
+if ($Twid < 1 || $Thgt < 1) {
+    fatal("invalid width and/or height");
+}
 my $verboseCommand = $verbose ? "set -x;" : "";
 
 if (@ARGV < 1) {
-    die("You must specify at least one color as an argument");
+    fatal("You must specify at least one color as an argument");
 } elsif (@ARGV < 2 && $norepeat) {
-    die("With the -norepeat option, you must specify at least two colors " .
-        "as arguments.");
+    fatal("With the -norepeat option, you must specify at least two colors " .
+          "as arguments.");
 }
 
 my @colorlist;
@@ -57,7 +65,7 @@ while (@colorlist >= 2) {
     my $rc = system("$verboseCommand pgmramp -lr $w $Thgt | " .
                     "pgmtoppm \"$colorlist[0]-$colorlist[1]\" >$outfile");
     if ($rc != 0) {
-        die("pgmramp|pgmtoppm failed.");
+        fatal("pgmramp|pgmtoppm failed.");
     }
     $widthRemaining -= $w;
     $n++;
diff --git a/lib/ppmdfont.c b/lib/ppmdfont.c
index a378f79c..c0db3f51 100644
--- a/lib/ppmdfont.c
+++ b/lib/ppmdfont.c
@@ -1,5 +1,7 @@
 #include <stdio.h>
 #include <assert.h>
+#include <errno.h>
+#include <string.h>
 
 #include "pm.h"
 #include "mallocvar.h"
@@ -62,7 +64,15 @@ static void
 readFontHeader(FILE *                   const ifP,
                struct ppmd_fontHeader * const fontHeaderP) {
     
-    fread(&fontHeaderP->signature, 1, sizeof(fontHeaderP->signature), ifP);
+    size_t rc;
+
+    rc = fread(&fontHeaderP->signature, 1, sizeof(fontHeaderP->signature),
+               ifP);
+
+    if (rc != sizeof(fontHeaderP->signature))
+        pm_error("Unable to read the header from the font file.  "
+                 "errno=%d (%s)", errno, strerror(errno));
+
     fontHeaderP->format         = fgetc(ifP);
     fontHeaderP->characterCount = fgetc(ifP);
     fontHeaderP->firstCodePoint = fgetc(ifP);
diff --git a/version.mk b/version.mk
index 02679c94..aa7209b8 100644
--- a/version.mk
+++ b/version.mk
@@ -1,4 +1,4 @@
 NETPBM_MAJOR_RELEASE = 10
 NETPBM_MINOR_RELEASE = 46
-NETPBM_POINT_RELEASE = 2
+NETPBM_POINT_RELEASE = 3