about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/Makefile6
-rw-r--r--editor/pamperspective.c1
-rw-r--r--editor/pamundice.c11
-rw-r--r--editor/pnmcomp.c460
-rw-r--r--editor/pnmconvol.c425
-rw-r--r--editor/pnmmontage.c1
-rw-r--r--editor/ppmcolormask.c1
-rw-r--r--editor/ppmdraw.c5
-rw-r--r--editor/specialty/pnmindex.c1
9 files changed, 280 insertions, 631 deletions
diff --git a/editor/Makefile b/editor/Makefile
index 398c4aea..c163f220 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -26,7 +26,7 @@ PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
 	       pbmclean pbmmask pbmpscale pbmreduce \
 	       pgmdeshadow pgmenhance \
 	       pgmmedian \
-	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop \
+	       pnmalias pnmcat pnmconvol pnmcrop \
 	       pnmgamma \
 	       pnmhisteq pnminvert pnmmontage \
 	       pnmnlfilt pnmnorm pnmpad pnmpaste \
@@ -94,3 +94,7 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	rm -f ppmquantall$(EXE) ; \
 	$(SYMLINK) pnmquantall ppmquantall
+# In August 2014, pamcomp replaced pnmcomp
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmcomp$(EXE) ; \
+	$(SYMLINK) pamcomp$(EXE) pnmcomp$(EXE)
diff --git a/editor/pamperspective.c b/editor/pamperspective.c
index a75e5243..16715c2e 100644
--- a/editor/pamperspective.c
+++ b/editor/pamperspective.c
@@ -18,6 +18,7 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is int string.h */
 
 #include <assert.h>
diff --git a/editor/pamundice.c b/editor/pamundice.c
index 93ce69f1..9a80e46d 100644
--- a/editor/pamundice.c
+++ b/editor/pamundice.c
@@ -491,7 +491,12 @@ openInStreams(struct pam         inpam[],
 /*----------------------------------------------------------------------------
    Open the input files for a single horizontal slice (there's one file
    for each vertical slice) and read the Netpbm headers from them.  Return
-   the pam structures to describe each.
+   the pam structures to describe each as inpam[].
+
+   Open the files for horizontal slice number 'rank', assuming there are
+   'fileCount' vertical slices (so open 'fileCount' files).  Use
+   inputFilePattern[] with each rank and file number to compute the name of
+   each file.
 -----------------------------------------------------------------------------*/
     unsigned int file;
 
@@ -507,7 +512,9 @@ openInStreams(struct pam         inpam[],
 static void
 closeInFiles(struct pam         pam[],
              unsigned int const fileCount) {
-
+/*----------------------------------------------------------------------------
+   Close the 'fileCount' input file streams represented by pam[].
+-----------------------------------------------------------------------------*/
     unsigned int file;
     
     for (file = 0; file < fileCount; ++file)
diff --git a/editor/pnmcomp.c b/editor/pnmcomp.c
deleted file mode 100644
index b782d69d..00000000
--- a/editor/pnmcomp.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/* +-------------------------------------------------------------------+ */
-/* | Copyright 1992, David Koblas.                                     | */
-/* |   Permission to use, copy, modify, and distribute this software   | */
-/* |   and its documentation for any purpose and without fee is hereby | */
-/* |   granted, provided that the above copyright notice appear in all | */
-/* |   copies and that both that copyright notice and this permission  | */
-/* |   notice appear in supporting documentation.  This software is    | */
-/* |   provided "as is" without express or implied warranty.           | */
-/* +-------------------------------------------------------------------+ */
-
-/*
-
-    DON'T ADD NEW FUNCTION TO THIS PROGRAM.  ADD IT TO pamcomp.c INSTEAD.
-
-*/
-
-
-
-#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
-#include <string.h>
-
-#include "pm_c_util.h"
-#include "pnm.h"
-#include "shhopt.h"
-#include "mallocvar.h"
-
-enum horizPos {BEYONDLEFT, LEFT, CENTER, RIGHT, BEYONDRIGHT};
-enum vertPos {ABOVE, TOP, MIDDLE, BOTTOM, BELOW};
-
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *underlyingFilespec;  /* '-' if stdin */
-    const char *overlayFilespec;
-    const char *alphaFilespec;
-    const char *outputFilespec;  /* '-' if stdout */
-    int xoff, yoff;   /* value of xoff, yoff options */
-    float opacity;
-    unsigned int alphaInvert;
-    enum horizPos align;
-    enum vertPos valign;
-};
-
-
-
-
-static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
-/*----------------------------------------------------------------------------
-   parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
-
-   If command line is internally inconsistent (invalid options, etc.),
-   issue error message to stderr and abort program.
-
-   Note that the strings we return are stored in the storage that
-   was passed to us as the argv array.  We also trash *argv.
------------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to pm_optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    char *align, *valign;
-    unsigned int xoffSpec, yoffSpec, alignSpec, valignSpec, opacitySpec,
-        alphaSpec;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "invert",     OPT_FLAG,   NULL,                  
-            &cmdlineP->alphaInvert,       0 );
-    OPTENT3(0, "xoff",       OPT_INT,    &cmdlineP->xoff,       
-            &xoffSpec,       0 );
-    OPTENT3(0, "yoff",       OPT_INT,    &cmdlineP->yoff,       
-            &yoffSpec,       0 );
-    OPTENT3(0, "opacity",    OPT_FLOAT, &cmdlineP->opacity,
-            &opacitySpec,       0 );
-    OPTENT3(0, "alpha",      OPT_STRING, &cmdlineP->alphaFilespec,
-            &alphaSpec,  0 );
-    OPTENT3(0, "align",      OPT_STRING, &align,
-            &alignSpec,  0 );
-    OPTENT3(0, "valign",     OPT_STRING, &valign,
-            &valignSpec,  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 */
-
-    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-
-    if (!xoffSpec)
-        cmdlineP->xoff = 0;
-    if (!yoffSpec)
-        cmdlineP->yoff = 0;
-    if (!alphaSpec)
-        cmdlineP->alphaFilespec = NULL;
-
-    if (alignSpec) {
-        if (strcasecmp(align, "BEYONDLEFT") == 0)
-            cmdlineP->align = BEYONDLEFT;
-        else if (strcasecmp(align, "LEFT") == 0)
-            cmdlineP->align = LEFT;
-        else if (strcasecmp(align, "CENTER") == 0)
-            cmdlineP->align = CENTER;
-        else if (strcasecmp(align, "RIGHT") == 0)
-            cmdlineP->align = RIGHT;
-        else if (strcasecmp(align, "BEYONDRIGHT") == 0)
-            cmdlineP->align = BEYONDRIGHT;
-        else
-            pm_error("Invalid value for align option: '%s'.  Only LEFT, "
-                     "RIGHT, CENTER, BEYONDLEFT, and BEYONDRIGHT are valid.", 
-                     align);
-    } else 
-        cmdlineP->align = LEFT;
-
-    if (valignSpec) {
-        if (strcasecmp(valign, "ABOVE") == 0)
-            cmdlineP->valign = ABOVE;
-        else if (strcasecmp(valign, "TOP") == 0)
-            cmdlineP->valign = TOP;
-        else if (strcasecmp(valign, "MIDDLE") == 0)
-            cmdlineP->valign = MIDDLE;
-        else if (strcasecmp(valign, "BOTTOM") == 0)
-            cmdlineP->valign = BOTTOM;
-        else if (strcasecmp(valign, "BELOW") == 0)
-            cmdlineP->valign = BELOW;
-        else
-            pm_error("Invalid value for valign option: '%s'.  Only TOP, "
-                     "BOTTOM, MIDDLE, ABOVE, and BELOW are valid.", 
-                     align);
-    } else 
-        cmdlineP->valign = TOP;
-
-    if (!opacitySpec) 
-        cmdlineP->opacity = 1.0;
-
-    if (argc-1 < 1)
-        pm_error("Need at least one argument: file specification of the "
-                 "overlay image.");
-
-    cmdlineP->overlayFilespec = argv[1];
-
-    if (argc-1 >= 2)
-        cmdlineP->underlyingFilespec = argv[2];
-    else
-        cmdlineP->underlyingFilespec = "-";
-
-    if (argc-1 >= 3)
-        cmdlineP->outputFilespec = argv[3];
-    else
-        cmdlineP->outputFilespec = "-";
-
-    if (argc-1 > 3)
-        pm_error("Too many arguments.  Only acceptable arguments are: "
-                 "overlay image, underlying image, output image");
-}
-
-
-
-
-static void
-warnOutOfFrame( int const originLeft,
-                int const originTop, 
-                int const overCols,
-                int const overRows,
-                int const underCols,
-                int const underRows ) {
-    if (originLeft >= underCols)
-        pm_message("WARNING: the overlay is entirely off the right edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The horizontal "
-                   "overlay position you selected is %d, "
-                   "and the underlying image "
-                   "is only %d pixels wide.", originLeft, underCols );
-    else if (originLeft + overCols <= 0)
-        pm_message("WARNING: the overlay is entirely off the left edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The horizontal "
-                   "overlay position you selected is %d and the overlay is "
-                   "only %d pixels wide.", originLeft, overCols);
-    else if (originTop >= underRows)
-        pm_message("WARNING: the overlay is entirely off the bottom edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The vertical "
-                   "overlay position you selected is %d, "
-                   "and the underlying image "
-                   "is only %d pixels high.", originTop, underRows );
-    else if (originTop + overRows <= 0)
-        pm_message("WARNING: the overlay is entirely off the top edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The vertical "
-                   "overlay position you selected is %d and the overlay is "
-                   "only %d pixels high.", originTop, overRows);
-}
-
-
-
-static void
-computeOverlayPosition(const int underCols, const int underRows,
-                       const int overCols, const int overRows,
-                       const struct cmdlineInfo cmdline, 
-                       int * const originLeftP,
-                       int * const originTopP) {
-/*----------------------------------------------------------------------------
-   Determine where to overlay the overlay image, based on the options the
-   user specified and the realities of the image dimensions.
-
-   The origin may be outside the underlying image (so e.g. *originLeftP may
-   be negative or > image width).  That means not all of the overlay image
-   actually gets used.  In fact, there may be no overlap at all.
------------------------------------------------------------------------------*/
-    int xalign, yalign;
-
-    switch (cmdline.align) {
-    case BEYONDLEFT:  xalign = -overCols;              break;
-    case LEFT:        xalign = 0;                      break;
-    case CENTER:      xalign = (underCols-overCols)/2; break;
-    case RIGHT:       xalign = underCols - overCols;   break;
-    case BEYONDRIGHT: xalign = underCols;              break;
-    }
-    switch (cmdline.valign) {
-    case ABOVE:       yalign = -overRows;              break;
-    case TOP:         yalign = 0;                      break;
-    case MIDDLE:      yalign = (underRows-overRows)/2; break;
-    case BOTTOM:      yalign = underRows - overRows;   break;
-    case BELOW:       yalign = underRows;              break;
-    }
-    *originLeftP = xalign + cmdline.xoff;
-    *originTopP  = yalign + cmdline.yoff;
-
-    warnOutOfFrame( *originLeftP, *originTopP, 
-                    overCols, overRows, underCols, underRows );    
-}
-
-
-
-static pixval
-composeComponents(pixval const compA, 
-                  pixval const compB,
-                  float  const distrib,
-                  pixval const maxval) {
-/*----------------------------------------------------------------------------
-  Compose a single component of each of two pixels, with 'distrib' being
-  the fraction of 'compA' in the result, 1-distrib the fraction of 'compB'.
-  
-  Both inputs are based on a maxval of 'maxval', and so is our result.
-  
-  Note that while 'distrib' in the straightforward case is always in
-  [0,1], it can in fact be negative or greater than 1.  We clip the
-  result as required to return a legal pixval.
------------------------------------------------------------------------------*/
-    return MIN(maxval, MAX(0, (int)compA * distrib +
-                              (int)compB * (1.0 - distrib) + 
-                              0.5
-                          )
-              );
-}
-
-
-
-static pixel
-composePixels(pixel  const pixelA,
-              pixel  const pixelB,
-              float  const distrib,
-              pixval const maxval) {
-/*----------------------------------------------------------------------------
-  Compose two pixels 'pixelA' and 'pixelB', with 'distrib' being the
-  fraction of 'pixelA' in the result, 1-distrib the fraction of 'pixelB'.
-
-  Both inputs are based on a maxval of 'maxval', and so is our result.
-  
-  Note that while 'distrib' in the straightforward case is always in
-  [0,1], it can in fact be negative or greater than 1.  We clip the
-  result as required to return a legal pixval.
------------------------------------------------------------------------------*/
-    pixel retval;
-
-    pixval const red = 
-        composeComponents(PPM_GETR(pixelA), PPM_GETR(pixelB), distrib, maxval);
-    pixval const grn =
-        composeComponents(PPM_GETG(pixelA), PPM_GETG(pixelB), distrib, maxval);
-    pixval const blu = 
-        composeComponents(PPM_GETB(pixelA), PPM_GETB(pixelB), distrib, maxval);
-
-    PPM_ASSIGN(retval, red, grn, blu);
-
-    return retval;
-}
-
-
-
-static void
-composite(int      const originleft, 
-          int      const origintop, 
-          pixel ** const overlayImage, 
-          int      const overlayCols, 
-          int      const overlayRows,
-          xelval   const overlayMaxval, 
-          int      const overlayType,
-          int      const cols, 
-          int      const rows, 
-          xelval   const maxval, 
-          int      const type,
-          gray **  const alpha, 
-          gray     const alphaMax, 
-          bool     const invertAlpha,
-          float    const opacity,
-          FILE *   const ifp, 
-          FILE *   const ofp) {
-/*----------------------------------------------------------------------------
-   Overlay the overlay image 'overlayImage' onto the underlying image
-   which is in file 'ifp', and output the composite to file 'ofp'.
-
-   The underlying image file 'ifp' is positioned after its header.  The
-   width, height, format, and maxval of the underlying image are 'cols',
-   'rows', 'type', and 'maxval'.
-
-   The width, height, format, and maxval of the overlay image are
-   overlayCols, overlayRows, overlayType and overlayMaxval.
-
-   'originleft' and 'origintop' are the coordinates in the underlying
-   image plane where the top left corner of the overlay image is
-   to go.  It is not necessarily inside the underlying image (in fact,
-   may be negative).  Only the part of the overlay that actually intersects
-   the underlying image, if any, gets into the output.
-
-   Note that we modify the overlay image 'overlayImage' to change its
-   format and maxval to the format and maxval of the output.
------------------------------------------------------------------------------*/
-    /* otype and oxmaxv are the type and maxval for the composed (output)
-       image, and are derived from that of the underlying and overlay
-       images.
-    */
-    int    const otype = (overlayType < type) ? type : overlayType;
-    xelval const omaxv = pm_lcm(maxval, overlayMaxval, 1, PNM_OVERALLMAXVAL);
-
-    int     row;
-    xel     *pixelrow;
-
-    pixelrow = pnm_allocrow(cols);
-
-    if (overlayType != otype || overlayMaxval != omaxv) {
-        pnm_promoteformat(overlayImage, overlayCols, overlayRows,
-                          overlayMaxval, overlayType, omaxv, otype);
-    }
-
-    pnm_writepnminit(ofp, cols, rows, omaxv, otype, 0);
-
-    for (row = 0; row < rows; ++row) {
-        int col;
-
-        /* Read a row and convert it to the output type */
-        pnm_readpnmrow(ifp, pixelrow, cols, maxval, type);
-
-        if (type != otype || maxval != omaxv)
-            pnm_promoteformatrow(pixelrow, cols, maxval, type, omaxv, otype);
-
-        /* Now overlay the overlay with alpha (if defined) */
-        for (col = 0; col < cols; ++col) {
-            int const ovlcol = col - originleft;
-            int const ovlrow = row - origintop;
-
-            double overlayWeight;
-
-            if (ovlcol >= 0 && ovlcol < overlayCols &&
-                ovlrow >= 0 && ovlrow < overlayRows) {
-
-                if (alpha == NULL) {
-                    overlayWeight = opacity;
-                } else {
-                    double alphaval;
-                    alphaval = 
-                        (double)alpha[ovlrow][ovlcol] / (double)alphaMax;
-                    if (invertAlpha)
-                        alphaval = 1.0 - alphaval;
-                    overlayWeight = alphaval * opacity;
-                }
-
-                pixelrow[col] = composePixels(overlayImage[ovlrow][ovlcol],
-                                              pixelrow[col], 
-                                              overlayWeight, omaxv);
-            }
-        }
-        pnm_writepnmrow(ofp, pixelrow, cols, omaxv, otype, 0);
-    }
-    pnm_freerow(pixelrow);
-}
-
-
-
-int
-main(int argc, char *argv[]) {
-
-    FILE    *ifp, *ofp;
-    pixel   **image;
-    int     imageCols, imageRows, imageType;
-    xelval  imageMax;
-    int     cols, rows, type;
-    xelval  maxval;
-    gray    **alpha;
-    int     alphaCols, alphaRows;
-    xelval  alphaMax;
-    struct cmdlineInfo cmdline;
-    int originLeft, originTop;
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-        
-    { /* Read the overlay image into 'image' */
-        FILE *fp;
-        fp = pm_openr(cmdline.overlayFilespec);
-        image = 
-            pnm_readpnm(fp, &imageCols, &imageRows, &imageMax, &imageType);
-        pm_close(fp);
-    }
-    if (cmdline.alphaFilespec) {
-        /* Read the alpha mask file into 'alpha' */
-        FILE *fp = pm_openr(cmdline.alphaFilespec);
-        alpha = pgm_readpgm(fp, &alphaCols, &alphaRows, &alphaMax);
-        pm_close(fp);
-            
-        if (imageCols != alphaCols || imageRows != alphaRows)
-            pm_error("Alpha map and overlay image are not the same size");
-    } else
-        alpha = NULL;
-
-    ifp = pm_openr(cmdline.underlyingFilespec);
-
-    ofp = pm_openw(cmdline.outputFilespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &type);
-
-    computeOverlayPosition(cols, rows, imageCols, imageRows, 
-                           cmdline, &originLeft, &originTop);
-
-    composite(originLeft, originTop,
-              image, imageCols, imageRows, imageMax, imageType, 
-              cols, rows, maxval, type, 
-              alpha, alphaMax, cmdline.alphaInvert, cmdline.opacity,
-              ifp, ofp);
-
-    pm_close(ifp);
-    pm_close(ofp);
-
-    /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
-    return 0;
-}
-
-
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index 105f4644..485fa0b8 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -27,6 +27,16 @@
 #include "pam.h"
 
 
+
+static sample const
+clipSample(sample const unclipped,
+           sample const maxval) {
+
+    return MIN(maxval, MAX(0, unclipped));
+}
+
+
+
 static void
 validateKernelDimensions(unsigned int const width,
                          unsigned int const height) {
@@ -67,6 +77,7 @@ struct cmdlineInfo {
     unsigned int matrixSpec;
     struct matrixOpt matrix;
     unsigned int normalize;
+    unsigned int bias;
 };
 
 
@@ -304,6 +315,7 @@ parseCommandLine(int argc, char ** argv,
     unsigned int option_def_index;
     unsigned int matrixfileSpec;
     const char * matrixOpt;
+    unsigned int biasSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -316,6 +328,8 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->nooffset,       0);
     OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
             &cmdlineP->normalize,      0);
+    OPTENT3(0, "bias",         OPT_UINT,   &cmdlineP->bias,
+            &biasSpec,                 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -324,6 +338,9 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    if (!biasSpec)
+        cmdlineP->bias = 0;
+
     if (matrixfileSpec && cmdlineP->matrixSpec)
         pm_error("You can't specify by -matrix and -matrixfile");
 
@@ -375,7 +392,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-struct convKernel {
+struct ConvKernel {
     unsigned int cols;
         /* Width of the convolution window */
     unsigned int rows;
@@ -393,12 +410,18 @@ struct convKernel {
            It can have magnitude greater than or less than one.  It can be
            positive or negative.  
         */
+    unsigned int bias;
+        /* The amount to be added to the linear combination of sample values.
+           We take a little liberty with the term "convolution kernel" to
+           include this value, since convolution per se does not involve any
+           such biasing.
+        */
 };
 
 
 
 static void
-warnBadKernel(struct convKernel * const convKernelP) {
+warnBadKernel(struct ConvKernel * const convKernelP) {
 
     float sum[3];
     unsigned int plane;
@@ -456,7 +479,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
                     tuple * const *      const ctuples, 
                     unsigned int         const depth,
                     bool                 const offsetPnm,
-                    struct convKernel ** const convKernelPP) {
+                    struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Compute the convolution matrix in normalized form from the PGM form
    'ctuples'/'cpamP'.  Each element of the output matrix is the actual weight
@@ -479,7 +502,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
     double const offset = offsetPnm ? - 1.0 : 0.0;
     unsigned int const planes = MIN(3, depth);
 
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     unsigned int plane;
 
     MALLOCVAR_NOFAIL(convKernelP);
@@ -507,13 +530,15 @@ convKernelCreatePnm(struct pam *         const cpamP,
             }
         }
     }
+    convKernelP->bias = 0;
+
     *convKernelPP = convKernelP;
 }
 
 
 
 static void
-convKernelDestroy(struct convKernel * const convKernelP) {
+convKernelDestroy(struct ConvKernel * const convKernelP) {
 
     unsigned int plane;
 
@@ -531,7 +556,7 @@ convKernelDestroy(struct convKernel * const convKernelP) {
 
 
 static void
-normalizeKernelPlane(struct convKernel * const convKernelP,
+normalizeKernelPlane(struct ConvKernel * const convKernelP,
                      unsigned int        const plane) {
 
     unsigned int row;
@@ -563,7 +588,7 @@ normalizeKernelPlane(struct convKernel * const convKernelP,
 
 
 static void
-normalizeKernel(struct convKernel * const convKernelP) {
+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.
@@ -580,7 +605,7 @@ static void
 getKernelPnm(const char *         const fileName,
              unsigned int         const depth,
              bool                 const offset,
-             struct convKernel ** const convKernelPP) {
+             struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Get the convolution kernel from the PNM file named 'fileName'.
    'offset' means the PNM convolution matrix is defined in offset form so
@@ -613,7 +638,8 @@ static void
 convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
                           bool                 const normalize,
                           unsigned int         const depth,
-                          struct convKernel ** const convKernelPP) {
+                          unsigned int         const bias,
+                          struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
    Create a convolution kernel as described by a -matrix command line
    option.
@@ -625,7 +651,7 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
    so they may form a biased matrix -- i.e. one which brightens or dims the
    image overall.
 -----------------------------------------------------------------------------*/
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     unsigned int plane;
 
     MALLOCVAR(convKernelP);
@@ -652,6 +678,8 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
     if (normalize)
         normalizeKernel(convKernelP);
 
+    convKernelP->bias = bias;
+
     *convKernelPP = convKernelP;
 }
 
@@ -821,7 +849,8 @@ static void
 convKernelCreateSimpleFile(const char **        const fileNameList,
                            bool                 const normalize,
                            unsigned int         const depth,
-                           struct convKernel ** const convKernelPP) {
+                           unsigned int         const bias,
+                           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
@@ -834,7 +863,7 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
    so they may form a biased matrix -- i.e. one which brightens or dims the
    image overall.
 -----------------------------------------------------------------------------*/
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     unsigned int fileCt;
     unsigned int planeCt;
     unsigned int plane;
@@ -887,6 +916,8 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
 
     convKernelP->cols = width;
     convKernelP->rows = height;
+    convKernelP->bias = bias;
+
     *convKernelPP = convKernelP;
 }
 
@@ -895,7 +926,7 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
 static void
 getKernel(struct cmdlineInfo   const cmdline,
           unsigned int         const depth,
-          struct convKernel ** const convKernelPP) {
+          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
@@ -904,17 +935,17 @@ getKernel(struct cmdlineInfo   const 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;
+    struct ConvKernel * convKernelP;
 
     if (cmdline.pnmMatrixFileName)
         getKernelPnm(cmdline.pnmMatrixFileName, depth, !cmdline.nooffset,
                      &convKernelP);
     else if (cmdline.matrixfile)
         convKernelCreateSimpleFile(cmdline.matrixfile, cmdline.normalize,
-                                   depth, &convKernelP);
+                                   depth, cmdline.bias, &convKernelP);
     else if (cmdline.matrixSpec)
         convKernelCreateMatrixOpt(cmdline.matrix, cmdline.normalize,
-                                  depth, &convKernelP);
+                                  depth, cmdline.bias, &convKernelP);
 
     warnBadKernel(convKernelP);
 
@@ -925,7 +956,7 @@ getKernel(struct cmdlineInfo   const cmdline,
 
 static void
 validateEnoughImageToConvolve(const struct pam *        const inpamP,
-                              const struct convKernel * const convKernelP) {
+                              const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
    Abort program if the image isn't big enough in both directions to have
    at least one convolved pixel.
@@ -1025,8 +1056,39 @@ readAndScaleRows(struct pam *              const inpamP,
 
 
 static void
+writePamRowBiased(struct pam * const outpamP,
+                  tuple *      const row,
+                  unsigned int const bias) {
+/*----------------------------------------------------------------------------
+   Write row[] to the output file according to *outpamP, but with
+   'bias' added to each sample value, clipped to maxval.
+-----------------------------------------------------------------------------*/
+    if (bias == 0)
+        pnm_writepamrow(outpamP, row);
+    else {
+        unsigned int col;
+
+        tuple * const outrow = pnm_allocpamrow(outpamP);
+
+        for (col = 0; col < outpamP->width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < outpamP->depth; ++plane) {
+                outrow[col][plane] =
+                    MIN(outpamP->maxval, bias + row[col][plane]);
+            }
+        }
+        pnm_writepamrow(outpamP, outrow);
+
+        pnm_freepamrow(outrow);
+    }
+}
+
+
+
+static void
 writeUnconvolvedTop(struct pam *              const outpamP,
-                    const struct convKernel * const convKernelP,
+                    const struct ConvKernel * const convKernelP,
                     tuple **                  const rowbuf) {
 /*----------------------------------------------------------------------------
    Write out the top part that we can't convolve because the convolution
@@ -1038,14 +1100,14 @@ writeUnconvolvedTop(struct pam *              const outpamP,
     unsigned int row;
 
     for (row = 0; row < convKernelP->rows/2; ++row)
-        pnm_writepamrow(outpamP, rowbuf[row]);
+        writePamRowBiased(outpamP, rowbuf[row], convKernelP->bias);
 }
 
 
 
 static void
 writeUnconvolvedBottom(struct pam *              const outpamP,
-                       const struct convKernel * const convKernelP,
+                       const struct ConvKernel * const convKernelP,
                        unsigned int              const windowHeight,
                        tuple **                  const circMap) {
 /*----------------------------------------------------------------------------
@@ -1061,7 +1123,7 @@ writeUnconvolvedBottom(struct pam *              const outpamP,
          row < windowHeight;
          ++row) {
 
-        pnm_writepamrow(outpamP, circMap[row]);
+        writePamRowBiased(outpamP, circMap[row], convKernelP->bias);
     }
 }
 
@@ -1093,7 +1155,7 @@ setupCircMap(tuple **     const circMap,
 static void
 convolveGeneralRowPlane(struct pam *              const pamP,
                         tuple **                  const window,
-                        const struct convKernel * const convKernelP,
+                        const struct ConvKernel * const convKernelP,
                         unsigned int              const plane,
                         tuple *                   const outputrow) {
 /*----------------------------------------------------------------------------
@@ -1114,7 +1176,10 @@ convolveGeneralRowPlane(struct pam *              const pamP,
     
     for (col = 0; col < pamP->width; ++col) {
         if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
         else {
             unsigned int const leftcol = col - ccolso2;
             unsigned int crow;
@@ -1127,7 +1192,8 @@ convolveGeneralRowPlane(struct pam *              const pamP,
                     sum += leftrptr[ccol][plane] *
                         convKernelP->weight[plane][crow][ccol];
             }
-            outputrow[col][plane] = MIN(pamP->maxval, MAX(0, sum + 0.5));
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + sum + 0.5, pamP->maxval);
         }
     }
 }
@@ -1137,7 +1203,7 @@ convolveGeneralRowPlane(struct pam *              const pamP,
 static void
 convolveGeneral(struct pam *              const inpamP,
                 struct pam *              const outpamP,
-                const struct convKernel * const convKernelP) {
+                const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
    Do the convolution without taking advantage of any useful redundancy in the
    convolution matrix.
@@ -1244,7 +1310,7 @@ freeSum(sample **    const sum,
 static void
 computeInitialColumnSums(struct pam *              const pamP,
                          tuple **                  const window,
-                         const struct convKernel * const convKernelP,
+                         const struct ConvKernel * const convKernelP,
                          sample **                 const convColumnSum) {
 /*----------------------------------------------------------------------------
   Add up the sum of each column of window[][], whose rows are described
@@ -1271,7 +1337,7 @@ computeInitialColumnSums(struct pam *              const pamP,
 
 
 static void
-convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
+convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
                               struct pam *              const pamP,
                               tuple **                  const window,
                               tuple *                   const outputrow,
@@ -1301,30 +1367,32 @@ convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
         unsigned int col;
         sample gisum;
 
-        gisum = 0;
-        for (col = 0; col < pamP->width; ++col) {
-            if (col < ccolso2 || col >= pamP->width - ccolso2)
-                outputrow[col][plane] = window[crowso2][col][plane];
-            else if (col == ccolso2) {
-                unsigned int const leftcol = col - ccolso2;
-
-                unsigned int ccol;
-
-                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
-                    gisum += convColumnSum[plane][leftcol + ccol];
-
+        for (col = 0, gisum = 0; col < pamP->width; ++col) {
+            if (col < ccolso2 || col >= pamP->width - ccolso2) {
+                /* The unconvolved left or right edge */
                 outputrow[col][plane] =
-                    MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                    clipSample(convKernelP->bias +
+                               window[crowso2][col][plane],
+                               pamP->maxval);
             } else {
-                /* Column numbers to subtract or add to isum */
-                unsigned int const subcol = col - ccolso2 - 1;
-                unsigned int const addcol = col + ccolso2;  
+                if (col == ccolso2) {
+                    unsigned int const leftcol = col - ccolso2;
 
-                gisum -= convColumnSum[plane][subcol];
-                gisum += convColumnSum[plane][addcol];
+                    unsigned int ccol;
 
+                    for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                        gisum += convColumnSum[plane][leftcol + ccol];
+                } else {
+                    /* Column numbers to subtract or add to isum */
+                    unsigned int const subcol = col - ccolso2 - 1;
+                    unsigned int const addcol = col + ccolso2;  
+
+                    gisum -= convColumnSum[plane][subcol];
+                    gisum += convColumnSum[plane][addcol];
+                }
                 outputrow[col][plane] =
-                    MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                    clipSample(convKernelP->bias + gisum * weight + 0.5,
+                               pamP->maxval);
             }
         }
     }
@@ -1334,7 +1402,7 @@ convolveRowWithColumnSumsMean(const struct convKernel * const convKernelP,
 
 static void
 convolveRowWithColumnSumsVertical(
-    const struct convKernel * const convKernelP,
+    const struct ConvKernel * const convKernelP,
     struct pam *              const pamP,
     tuple **                  const window,
     tuple *                   const outputrow,
@@ -1363,9 +1431,13 @@ convolveRowWithColumnSumsVertical(
         unsigned int col;
     
         for (col = 0; col < pamP->width; ++col) {
-            if (col < ccolso2 || col >= pamP->width - ccolso2)
-                outputrow[col][plane] = window[crowso2][col][plane];
-            else {
+            if (col < ccolso2 || col >= pamP->width - ccolso2) {
+                /* The unconvolved left or right edge */
+                outputrow[col][plane] =
+                    clipSample(convKernelP->bias +
+                               window[crowso2][col][plane],
+                               pamP->maxval);
+            } else {
                 unsigned int const leftcol = col - ccolso2;
                 unsigned int ccol;
                 float sum;
@@ -1376,7 +1448,8 @@ convolveRowWithColumnSumsVertical(
                     sum += convColumnSum[plane][leftcol + ccol] *
                         convKernelP->weight[plane][0][ccol];
 
-                outputrow[col][plane] = MIN(pamP->maxval, MAX(0, sum + 0.5));
+                outputrow[col][plane] =
+                    clipSample(convKernelP->bias + sum + 0.5, pamP->maxval);
             }
         }
     }
@@ -1387,7 +1460,7 @@ convolveRowWithColumnSumsVertical(
 static void
 convolveMeanRowPlane(struct pam *              const pamP,
                      tuple **                  const window,
-                     const struct convKernel * const convKernelP,
+                     const struct ConvKernel * const convKernelP,
                      unsigned int              const plane,
                      tuple *                   const outputrow,
                      sample *                  const convColumnSum) {
@@ -1417,38 +1490,40 @@ convolveMeanRowPlane(struct pam *              const pamP,
     unsigned int col;
     sample gisum;
 
-    gisum = 0;
-    for (col = 0; col < pamP->width; ++col) {
-        if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
-        else if (col == ccolso2) {
-            unsigned int const leftcol = col - ccolso2;
-
-            unsigned int ccol;
-
-            for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
-                sample * const thisColumnSumP =
-                    &convColumnSum[leftcol + ccol];
-                *thisColumnSumP = *thisColumnSumP
-                    - window[subrow][ccol][plane]
-                    + window[addrow][ccol][plane];
-                gisum += *thisColumnSumP;
-            }
+    for (col = 0, gisum = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
         } else {
-            /* Column numbers to subtract or add to isum */
-            unsigned int const subcol = col - ccolso2 - 1;
-            unsigned int const addcol = col + ccolso2;  
-
-            convColumnSum[addcol] = convColumnSum[addcol]
-                - window[subrow][addcol][plane]
-                + window[addrow][addcol][plane];
+            if (col == ccolso2) {
+                unsigned int const leftcol = col - ccolso2;
 
-            gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
+                unsigned int ccol;
 
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                    sample * const thisColumnSumP =
+                        &convColumnSum[leftcol + ccol];
+                    *thisColumnSumP = *thisColumnSumP
+                        - window[subrow][ccol][plane]
+                        + window[addrow][ccol][plane];
+                    gisum += *thisColumnSumP;
+                }
+            } else {
+                /* Column numbers to subtract or add to isum */
+                unsigned int const subcol = col - ccolso2 - 1;
+                unsigned int const addcol = col + ccolso2;  
+                
+                convColumnSum[addcol] = convColumnSum[addcol]
+                    - window[subrow][addcol][plane]
+                    + window[addrow][addcol][plane];
+                
+                gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
+            }
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, gisum * weight + 0.5));
+                clipSample(convKernelP->bias + gisum * weight + 0.5,
+                           pamP->maxval);
         }
     }
 }
@@ -1457,7 +1532,7 @@ convolveMeanRowPlane(struct pam *              const pamP,
 
 typedef void convolver(struct pam *              const inpamP,
                        struct pam *              const outpamP,
-                       const struct convKernel * const convKernelP);
+                       const struct ConvKernel * const convKernelP);
 
 
 
@@ -1466,7 +1541,7 @@ static convolver convolveMean;
 static void
 convolveMean(struct pam *              const inpamP,
              struct pam *              const outpamP,
-             const struct convKernel * const convKernelP) {
+             const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
   Mean Convolution
 
@@ -1645,7 +1720,7 @@ freeRowSum(sample ***   const sum,
 static void
 convolveHorizontalRowPlane0(struct pam *              const outpamP,
                             tuple **                  const window,
-                            const struct convKernel * const convKernelP,
+                            const struct ConvKernel * const convKernelP,
                             unsigned int              const plane,
                             tuple *                   const outputrow,
                             sample **                 const sumWindow) {
@@ -1659,47 +1734,57 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
     unsigned int col;
 
     for (col = 0; col < outpamP->width; ++col) {
-        if (col < ccolso2 || col >= outpamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
-        else if (col == ccolso2) {
-            /* This is the first column for which the entire convolution
-               kernel fits within the image horizontally.  I.e. the window
-               starts at the left edge of the image.
-            */
-            unsigned int const leftcol = 0;
-            
+        if (col < ccolso2 || col >= outpamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           outpamP->maxval);
+        } else {
             float matrixSum;
-            unsigned int crow;
 
-            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
-                tuple * const tuplesInWindow = &window[crow][leftcol];
+            if (col == ccolso2) {
+                /* This is the first column for which the entire convolution
+                   kernel fits within the image horizontally.  I.e. the window
+                   starts at the left edge of the image.
+                */
+                unsigned int const leftcol = 0;
+            
+                unsigned int crow;
 
-                unsigned int ccol;
-                
-                sumWindow[crow][col] = 0;
-                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
-                    sumWindow[crow][col] += tuplesInWindow[ccol][plane];
-                matrixSum +=
-                    sumWindow[crow][col] * convKernelP->weight[plane][crow][0];
-            }
-            outputrow[col][plane] =
-                MIN(outpamP->maxval, MAX(0, matrixSum + 0.5));
-        } else {
-            unsigned int const subcol  = col - ccolso2 - 1;
-            unsigned int const addcol  = col + ccolso2;
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    tuple * const tuplesInWindow = &window[crow][leftcol];
 
-            float matrixSum;
-            unsigned int crow;
+                    unsigned int ccol;
+                
+                    sumWindow[crow][col] = 0;
+                    for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                        sumWindow[crow][col] += tuplesInWindow[ccol][plane];
+                    matrixSum +=
+                        sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
+                }
+            } else {
+                unsigned int const subcol  = col - ccolso2 - 1;
+                unsigned int const addcol  = col + ccolso2;
 
-            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
-                sumWindow[crow][col] = sumWindow[crow][col-1] +
-                    + window[crow][addcol][plane]
-                    - window[crow][subcol][plane];
-                matrixSum +=
-                    sumWindow[crow][col] * convKernelP->weight[plane][crow][0];
+                unsigned int crow;
+                
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    sumWindow[crow][col] = sumWindow[crow][col-1] +
+                        + window[crow][addcol][plane]
+                        - window[crow][subcol][plane];
+                    matrixSum +=
+                        sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
+                }
             }
             outputrow[col][plane] =
-                MIN(outpamP->maxval, MAX(0, matrixSum + 0.5));
+                clipSample(convKernelP->bias + matrixSum + 0.5,
+                           outpamP->maxval);
         }
     }
 }
@@ -1737,7 +1822,7 @@ setupCircMap2(tuple **     const rowbuf,
 static void
 convolveHorizontalRowPlane(struct pam *              const pamP,
                            tuple **                  const window,
-                           const struct convKernel * const convKernelP,
+                           const struct ConvKernel * const convKernelP,
                            unsigned int              const plane,
                            tuple *                   const outputrow,
                            sample **                 const sumWindow) {
@@ -1763,14 +1848,16 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
-        if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = window[crowso2][col][plane];
-        else if (col == ccolso2) {
+        float matrixSum;
+
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
+        } else if (col == ccolso2) {
             unsigned int const leftcol = 0;
                 /* Window is up againt left edge of image */
 
-            float matrixSum;
-
             {
                 unsigned int ccol;
                 sumWindow[newrow][col] = 0;
@@ -1787,13 +1874,10 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
                         convKernelP->weight[plane][crow][0];
                 }
             }
-            outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         } else {
             unsigned int const subcol  = col - ccolso2 - 1;
             unsigned int const addcol  = col + ccolso2;  
 
-            float matrixSum;
             unsigned int crow;
 
             sumWindow[newrow][col] =
@@ -1805,9 +1889,9 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
                 matrixSum += sumWindow[crow][col] *
                     convKernelP->weight[plane][crow][0];
             }
-            outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
         }
+        outputrow[col][plane] =
+            clipSample(convKernelP->bias + matrixSum + 0.5, pamP->maxval);
     }
 }
 
@@ -1818,7 +1902,7 @@ static convolver convolveHorizontal;
 static void
 convolveHorizontal(struct pam *              const inpamP,
                    struct pam *              const outpamP,
-                   const struct convKernel * const convKernelP) {
+                   const struct ConvKernel * const convKernelP) {
 /*----------------------------------------------------------------------------
   Horizontal Convolution
 
@@ -1909,7 +1993,7 @@ convolveHorizontal(struct pam *              const inpamP,
 static void
 convolveVerticalRowPlane(struct pam *              const pamP,
                          tuple **                  const circMap,
-                         const struct convKernel * const convKernelP,
+                         const struct ConvKernel * const convKernelP,
                          unsigned int              const plane,
                          tuple *                   const outputrow,
                          sample *                  const convColumnSum) {
@@ -1927,45 +2011,54 @@ convolveVerticalRowPlane(struct pam *              const pamP,
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
-        if (col < ccolso2 || col >= pamP->width - ccolso2)
-            outputrow[col][plane] = circMap[crowso2][col][plane];
-        else if (col == ccolso2) {
-            unsigned int const leftcol = 0;
-                /* Convolution window is againt left edge of image */
-
-            float matrixSum;
-            unsigned int ccol;
-
-            /* 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];
-                convColumnSum[leftcol + ccol] -=
-                    circMap[subrow][leftcol + ccol][plane];
-            }
-            for (ccol = 0, matrixSum = 0.0; ccol < convKernelP->cols; ++ccol) {
-                matrixSum += convColumnSum[leftcol + ccol] *
-                    convKernelP->weight[plane][0][ccol];
-            }
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
+                clipSample(convKernelP->bias + circMap[crowso2][col][plane],
+                           pamP->maxval);
         } else {
-            unsigned int const leftcol = col - ccolso2;
-            unsigned int const addcol  = col + ccolso2;
-
             float matrixSum;
-            unsigned int ccol;
 
-            /* Slide window down in the column that just entered the window */
-            convColumnSum[addcol] += circMap[addrow][addcol][plane];
-            convColumnSum[addcol] -= circMap[subrow][addcol][plane];
+            if (col == ccolso2) {
+                unsigned int const leftcol = 0;
+                    /* Convolution window is againt left edge of image */
+
+                unsigned int ccol;
 
-            for (ccol = 0, matrixSum = 0.0; ccol < convKernelP->cols; ++ccol) {
-                matrixSum += convColumnSum[leftcol + ccol] *
-                    convKernelP->weight[plane][0][ccol];
+                /* 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];
+                    convColumnSum[leftcol + ccol] -=
+                        circMap[subrow][leftcol + ccol][plane];
+                }
+                for (ccol = 0, matrixSum = 0.0;
+                     ccol < convKernelP->cols;
+                     ++ccol) {
+                    matrixSum += convColumnSum[leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+                }
+            } else {
+                unsigned int const leftcol = col - ccolso2;
+                unsigned int const addcol  = col + ccolso2;
+
+                unsigned int ccol;
+
+                /* Slide window down in the column that just entered the
+                   window
+                */
+                convColumnSum[addcol] += circMap[addrow][addcol][plane];
+                convColumnSum[addcol] -= circMap[subrow][addcol][plane];
+
+                for (ccol = 0, matrixSum = 0.0;
+                     ccol < convKernelP->cols;
+                     ++ccol) {
+                    matrixSum += convColumnSum[leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+                }
             }
             outputrow[col][plane] =
-                MIN(pamP->maxval, MAX(0, matrixSum + 0.5));
+                clipSample(convKernelP->bias + matrixSum + 0.5, pamP->maxval);
         }
     }
 }
@@ -1977,7 +2070,7 @@ static convolver convolveVertical;
 static void
 convolveVertical(struct pam *              const inpamP,
                  struct pam *              const outpamP,
-                 const struct convKernel * const convKernelP) {
+                 const struct ConvKernel * const convKernelP) {
 
     /* Uses column sums as in mean convolution, above */
 
@@ -2058,7 +2151,7 @@ struct convolveType {
 
 
 static bool
-convolutionIncludesHorizontal(const struct convKernel * const convKernelP) {
+convolutionIncludesHorizontal(const struct ConvKernel * const convKernelP) {
 
     bool horizontal;
     unsigned int row;
@@ -2086,7 +2179,7 @@ convolutionIncludesHorizontal(const struct convKernel * const convKernelP) {
 
 
 static bool
-convolutionIncludesVertical(const struct convKernel * const convKernelP) {
+convolutionIncludesVertical(const struct ConvKernel * const convKernelP) {
 
     bool vertical;
     unsigned int col;
@@ -2114,7 +2207,7 @@ convolutionIncludesVertical(const struct convKernel * const convKernelP) {
 
 
 static void
-determineConvolveType(const struct convKernel * const convKernelP,
+determineConvolveType(const struct ConvKernel * const convKernelP,
                       struct convolveType *     const typeP) {
 /*----------------------------------------------------------------------------
    Determine which form of convolution is best to convolve the kernel
@@ -2150,7 +2243,7 @@ main(int argc, char * argv[]) {
     struct cmdlineInfo cmdline;
     FILE * ifP;
     struct convolveType convolveType;
-    struct convKernel * convKernelP;
+    struct ConvKernel * convKernelP;
     struct pam inpam;
     struct pam outpam;
 
diff --git a/editor/pnmmontage.c b/editor/pnmmontage.c
index 47827610..e54afc45 100644
--- a/editor/pnmmontage.c
+++ b/editor/pnmmontage.c
@@ -10,6 +10,7 @@
  * implied warranty.
  */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
 #include <limits.h>
diff --git a/editor/ppmcolormask.c b/editor/ppmcolormask.c
index 4e462f3e..31fbff2a 100644
--- a/editor/ppmcolormask.c
+++ b/editor/ppmcolormask.c
@@ -9,6 +9,7 @@
   Contributed to the public domain by its author.
 =========================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
 #include <string.h>
diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c
index c733ffcb..63d781ec 100644
--- a/editor/ppmdraw.c
+++ b/editor/ppmdraw.c
@@ -1,5 +1,6 @@
-#define _XOPEN_SOURCE    /* Make sure M_PI is in math.h */
-#define _BSD_SOURCE      /* Make sure strdup is in string.h */
+#define _XOPEN_SOURCE 500 
+   /* Make sure M_PI is in math.h, strdup is in string.h */
+#define _BSD_SOURCE      /* Make sure strdup is in string.h (alternate) */
 
 #include <string.h>
 #include <ctype.h>
diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c
index 1909c93a..4ec9edaa 100644
--- a/editor/specialty/pnmindex.c
+++ b/editor/specialty/pnmindex.c
@@ -14,6 +14,7 @@
 
 ============================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is in string.h */
 
 #include <assert.h>