about summary refs log tree commit diff
path: root/editor/specialty
diff options
context:
space:
mode:
Diffstat (limited to 'editor/specialty')
-rw-r--r--editor/specialty/pampaintspill.c172
-rw-r--r--editor/specialty/pampop9.c3
-rw-r--r--editor/specialty/pgmabel.c18
-rw-r--r--editor/specialty/pnmindex.c106
-rw-r--r--editor/specialty/ppmshift.c235
-rw-r--r--editor/specialty/ppmspread.c206
6 files changed, 483 insertions, 257 deletions
diff --git a/editor/specialty/pampaintspill.c b/editor/specialty/pampaintspill.c
index eb1888f7..7490fcef 100644
--- a/editor/specialty/pampaintspill.c
+++ b/editor/specialty/pampaintspill.c
@@ -6,7 +6,7 @@
  *
  * ----------------------------------------------------------------------
  *
- * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ * Copyright (C) 2010-2021 Scott Pakin <scott+pbm@pakin.org>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -45,11 +45,11 @@
 
 #include "mallocvar.h"
 #include "nstring.h"
+#include "rand.h"
 #include "shhopt.h"
 #include "pam.h"
 #include "pammap.h"
 
-
 static time_t const timeUpdateDelta = 30;
     /* Seconds between progress updates */
 static int const    minUpdates = 4;
@@ -67,6 +67,10 @@ struct cmdlineInfo {
     unsigned int all;
     float        power;
     unsigned int downsample;
+    unsigned int randomseedSpec;
+    unsigned int randomseed;
+    unsigned int nearSpec;
+    unsigned int near;
 };
 
 struct coords {
@@ -98,16 +102,20 @@ parseCommandLine(int argc, const char ** const argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
     option_def_index = 0;          /* Incremented by OPTENTRY */
 
-    OPTENT3(0, "bgcolor",    OPT_STRING, &cmdlineP->bgcolor,    
+    OPTENT3(0, "bgcolor",    OPT_STRING, &cmdlineP->bgcolor,
             &bgcolorSpec, 0);
     OPTENT3(0, "wrap",       OPT_FLAG,   NULL,
             &cmdlineP->wrap,       0);
     OPTENT3(0, "all",        OPT_FLAG,   NULL,
             &cmdlineP->all,        0);
-    OPTENT3(0, "power",      OPT_FLOAT,  &cmdlineP->power,      
+    OPTENT3(0, "power",      OPT_FLOAT,  &cmdlineP->power,
             &powerSpec, 0);
-    OPTENT3(0, "downsample", OPT_UINT,   &cmdlineP->downsample, 
+    OPTENT3(0, "downsample", OPT_UINT,   &cmdlineP->downsample,
             &downsampleSpec, 0);
+    OPTENT3(0, "randomseed", OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "near",       OPT_UINT,   &cmdlineP->near,
+            &cmdlineP->nearSpec, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = 0;
@@ -124,6 +132,11 @@ parseCommandLine(int argc, const char ** const argv,
     if (!downsampleSpec)
         cmdlineP->downsample = 0;
 
+    if (cmdlineP->nearSpec) {
+        if (cmdlineP->near == 0)
+            pm_error("The -near option requires a positive argument");
+    }
+
     if (argc-1 < 1)
         cmdlineP->inputFilename = "-";
     else {
@@ -223,7 +236,9 @@ locatePaintSources(struct pam *            const pamP,
                    tuple **                const tuples,
                    tuple                   const bgColor,
                    unsigned int            const downsample,
-                   struct paintSourceSet * const paintSourcesP) {
+                   struct paintSourceSet * const paintSourcesP,
+                   bool                    const randomseedSpec,
+                   unsigned int            const randomseed) {
 /*--------------------------------------------------------------------
   Construct a list of all pixel coordinates in the input image that
   represent a non-background color.
@@ -248,21 +263,24 @@ locatePaintSources(struct pam *            const pamP,
     pm_message("Image contains %u background + %u non-background pixels",
                pamP->width * pamP->height - paintSources.size,
                paintSources.size);
-    
+
     /* Reduce the number of paint sources to reduce execution time. */
     if (downsample > 0 && downsample < paintSources.size) {
+        struct pm_randSt randSt;
         unsigned int i;
 
-        srand(pm_randseed());
+        pm_randinit(&randSt);
+        pm_srand2(&randSt, randomseedSpec, randomseed);
 
         for (i = 0; i < downsample; ++i) {
             unsigned int const swapIdx =
-                i + rand() % (paintSources.size - i);
+                i + pm_rand(&randSt) % (paintSources.size - i);
             struct coords const swapVal = paintSources.list[i];
 
             paintSources.list[i] = paintSources.list[swapIdx];
             paintSources.list[swapIdx] = swapVal;
         }
+        pm_randterm(&randSt);
         paintSources.size = downsample;
     }
 
@@ -281,7 +299,7 @@ euclideanDistanceSqr(const struct coords * const p0,
                      unsigned int          const width,
                      unsigned int          const height) {
 /*----------------------------------------------------------------------------
-   Return the square of the Euclidian distance between p0 and p1.
+   Return the square of the Euclidean distance between p0 and p1.
 -----------------------------------------------------------------------------*/
     double const deltax = (double) (int) (p1->x - p0->x);
     double const deltay = (double) (int) (p1->y - p0->y);
@@ -299,7 +317,7 @@ euclideanDistanceTorusSqr(const struct coords * const p0,
                           unsigned int          const width,
                           unsigned int          const height) {
 /*----------------------------------------------------------------------------
-   Return the square of the Euclidian distance between p0 and p1, assuming
+   Return the square of the Euclidean distance between p0 and p1, assuming
    it's a toroidal surface on which the top row curves around to meet the
    bottom and the left column to the right.
 -----------------------------------------------------------------------------*/
@@ -356,6 +374,70 @@ reportProgress(unsigned int const rowsComplete,
 
 
 
+struct distanceList {
+    struct coords * sources;  /* malloc'ed */
+        /* The list of places in the image from which paint comes */
+    double * distSqrs;        /* malloc'ed */
+        /* The list of squared distances from the current point */
+    unsigned int size;
+        /* Number of entries in sources[] */
+};
+
+
+
+static void
+computeDistances(struct pam *           const pamP,
+                 struct coords          const target,
+                 struct paintSourceSet  const paintSources,
+                 distFunc_t *           const distFunc,
+                 bool                   const nearOnly,
+                 unsigned int           const numNear,
+                 struct distanceList *  const distancesP) {
+
+    unsigned int ps;
+
+    /* Acquire a list of all distances. */
+    distancesP->size = 0;
+    for (ps = 0; ps < paintSources.size; ++ps) {
+        struct coords const source = paintSources.list[ps];
+        double const distSqr =
+            (*distFunc)(&target, &source,
+                        pamP->width, pamP->height);
+        distancesP->sources[distancesP->size]  = source;
+        distancesP->distSqrs[distancesP->size] = distSqr;
+        ++distancesP->size;
+    }
+
+    /* If requested, truncate the list to the smallest numNear distances. */
+    if (nearOnly && numNear < distancesP->size) {
+        unsigned int i;
+
+        /* Perform a partial sort -- just enough to identify the numNear
+           smallest distances.  For performance reasons we assume that
+           numNear is much less than paintSources.size (say, less than
+           log2(paintSources.size)).
+        */
+        for (i = 0; i < numNear; ++i) {
+            unsigned int j;
+            for (j = i + 1; j < distancesP->size; ++j) {
+                if (distancesP->distSqrs[i] > distancesP->distSqrs[j]) {
+                    /* Swap elements i and j. */
+                    struct coords const src   = distancesP->sources[i];;
+                    double        const dist2 = distancesP->distSqrs[i];
+
+                    distancesP->sources[i]  = distancesP->sources[j];
+                    distancesP->distSqrs[i] = distancesP->distSqrs[j];
+                    distancesP->sources[j]  = src;
+                    distancesP->distSqrs[j] = dist2;
+                }
+            }
+        }
+        distancesP->size = numNear;
+    }
+}
+
+
+
 static void
 spillOnePixel(struct pam *          const pamP,
               struct coords         const target,
@@ -363,37 +445,38 @@ spillOnePixel(struct pam *          const pamP,
               distFunc_t *          const distFunc,
               double                const distPower,
               tuple                 const outTuple,
-              double *              const newColor) {
+              double *              const newColor,
+              bool                  const nearOnly,
+              unsigned int          const numNear,
+              struct distanceList * const distancesP) {
 
     unsigned int plane;
-    unsigned int ps;
+    unsigned int d;
     double       totalWeight;
 
     for (plane = 0; plane < pamP->depth; ++plane)
         newColor[plane] = 0.0;
+    computeDistances(pamP, target, paintSources, distFunc,
+                     nearOnly, numNear, distancesP);
     totalWeight = 0.0;
-    for (ps = 0; ps < paintSources.size; ++ps) {
-        struct coords const source = paintSources.list[ps];
-        double const distSqr =
-            (*distFunc)(&target, &source,
-                        pamP->width, pamP->height);
+    for (d = 0; d < distancesP->size; ++d) {
+        double        const distSqr = distancesP->distSqrs[d];
+        struct coords const source  = distancesP->sources[d];
 
-        if (distSqr > 0.0) {
-            /* We do special cases for some common cases with code
-               that is much faster than pow().
-            */
-            double const weight =
-                distPower == -2.0 ? 1.0 / distSqr :
-                distPower == -1.0 ? 1.0 / sqrt(distSqr):
-                pow(distSqr, distPower/2);
+        /* We do special cases for some common cases with code
+           that is much faster than pow().
+        */
+        double const weight =
+            distPower == -2.0 ? 1.0 / distSqr :
+            distPower == -1.0 ? 1.0 / sqrt(distSqr):
+            pow(distSqr, distPower/2);
 
-            unsigned int plane;
+        unsigned int plane;
 
-            for (plane = 0; plane < pamP->depth; ++plane)
-                newColor[plane] += weight * source.color[plane];
+        for (plane = 0; plane < pamP->depth; ++plane)
+            newColor[plane] += weight * source.color[plane];
 
-            totalWeight += weight;
-        }
+        totalWeight += weight;
     }
     for (plane = 0; plane < pamP->depth; ++plane)
         outTuple[plane] = (sample) (newColor[plane] / totalWeight);
@@ -409,6 +492,8 @@ produceOutputImage(struct pam *          const pamP,
                    distFunc_t *          const distFunc,
                    double                const distPower,
                    bool                  const all,
+                   bool                  const nearOnly,
+                   unsigned int          const numNear,
                    tuple ***             const outtuplesP) {
 /*--------------------------------------------------------------------
   Color each background pixel (or, if allPixels is 1, all pixels)
@@ -424,10 +509,14 @@ produceOutputImage(struct pam *          const pamP,
     rowsComplete = 0;
     #pragma omp parallel for
     for (row = 0; row < pamP->height; ++row) {
-        struct coords   target;
-        double        * newColor;
-        
+        struct coords         target;
+        double *              newColor;   /* malloc'ed */
+        struct distanceList * distancesP; /* malloc'ed */
+
         MALLOCARRAY(newColor, pamP->depth);
+        MALLOCVAR_NOFAIL(distancesP);
+        MALLOCARRAY_NOFAIL(distancesP->sources,  paintSources.size);
+        MALLOCARRAY_NOFAIL(distancesP->distSqrs, paintSources.size);
 
         target.y = row;
         for (target.x = 0; target.x < pamP->width; ++target.x) {
@@ -436,13 +525,17 @@ produceOutputImage(struct pam *          const pamP,
 
             if (all || tupleEqualColor(pamP, targetTuple, bgColor))
                 spillOnePixel(pamP, target, paintSources, distFunc, distPower,
-                              outputTuple, newColor);
+                              outputTuple, newColor, nearOnly, numNear,
+                              distancesP);
             else
                 pnm_assigntuple(pamP,  outputTuple, targetTuple);
         }
         #pragma omp critical (rowTally)
         reportProgress(++rowsComplete, pamP->height);
 
+        free(distancesP->distSqrs);
+        free(distancesP->sources);
+        free(distancesP);
         free(newColor);
     }
     *outtuplesP = outtuples;
@@ -484,10 +577,12 @@ main(int argc, const char *argv[]) {
                pnm_colorname(&inPam, bgColor, PAM_COLORNAME_HEXOK));
 
     locatePaintSources(&inPam, inTuples, bgColor, cmdline.downsample,
-                       &paintSources);
+                       &paintSources,
+                       cmdline.randomseedSpec, cmdline.randomseed);
 
     produceOutputImage(&inPam, inTuples, bgColor, paintSources, distFunc,
-                       cmdline.power, cmdline.all, &outTuples);
+                       cmdline.power, cmdline.all,
+                       cmdline.nearSpec, cmdline.near, &outTuples);
 
     outPam = inPam;
     outPam.file = stdout;
@@ -498,3 +593,6 @@ main(int argc, const char *argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/editor/specialty/pampop9.c b/editor/specialty/pampop9.c
index d6c61e4f..b92c7d6b 100644
--- a/editor/specialty/pampop9.c
+++ b/editor/specialty/pampop9.c
@@ -25,9 +25,6 @@
 
 #include "pam.h"
 
-static const char * const copyright = 
-  "(c) Robert Tinsley 2003 (http://www.thepoacher.net/contact)";
-
 static const char *usagestr = "pnmfile|- xtiles ytiles xdelta ydelta";
 
 int main(int argc, char *argv[])
diff --git a/editor/specialty/pgmabel.c b/editor/specialty/pgmabel.c
index 0f4233ac..5badfd19 100644
--- a/editor/specialty/pgmabel.c
+++ b/editor/specialty/pgmabel.c
@@ -44,7 +44,7 @@ static const char* const version="$VER: pgmabel 1.009 (24 Jan 2002)";
 #include "pgm.h"
 
 #ifndef PID2          /*  PI/2 (on AMIGA always defined) */
-#define PID2    1.57079632679489661923  
+#define PID2    1.57079632679489661923
 #endif
 
 
@@ -59,7 +59,7 @@ static double *aldl, *ardl;                /* pointer for weighting factors */
 **      xr    <-  array of the calculated elements of the row
 **      adl   <-  pre-calculated surface coefficient for each segment
 */
-static double 
+static double
 Sum ( int n, double *xr, int N, double *adl)
 {
     int k;
@@ -79,7 +79,7 @@ Sum ( int n, double *xr, int N, double *adl)
 **      R, N  <-  indizes of the coefficient
 **      r     <-  radial position of the center of the surface
 */
-static double 
+static double
 dr ( int R, double r,  int N)
 {
     double a;
@@ -95,7 +95,7 @@ dr ( int R, double r,  int N)
 **        N    <-  width of the array
 **        adl  <-  array with pre-calculated weighting factors
 */
-static void 
+static void
 abel ( float *y, int N, double *adl)
 {
     register int n;
@@ -126,7 +126,7 @@ abel ( float *y, int N, double *adl)
 /* ----------------------------------------------------------------------------
 ** printing a help message if Option -h(elp) is chosen
 */
-static void 
+static void
 help()
 {
     pm_message("-----------------------------------------------------------------");
@@ -142,7 +142,7 @@ help()
     pm_message("|   verbose : output of useful data                             |");
     pm_message("|   pgmfile : Name of a pgmfile (optional)                      |");
     pm_message("|                                                               |");
-    pm_message("| for further information please contact the manpage            |"); 
+    pm_message("| for further information please contact the manpage            |");
     pm_message("-----------------------------------------------------------------");
     pm_message("%s",version);     /* telling the version      */
     exit(-1);                     /* retur-code for no result */
@@ -267,11 +267,11 @@ int main( argc, argv )
     }
     for (col = 0; col < (cols-midcol); ++col)      /* factors for right side */
     {
-        for (tc = 0; tc < (cols-midcol); ++tc) 
+        for (tc = 0; tc < (cols-midcol); ++tc)
             ardl[col*(cols-midcol)+tc] = dr(col,tc+0.5,cols-midcol);
     }
 
-    /* abel-transformation for each row splitted in right and left side      */
+    /* abel-transformation for each row split into right and left side      */
     for ( row = 0; row < rows ; ++row )
     {
         pgm_readpgmrow( ifp, grayorig, cols, maxval, format );
@@ -308,3 +308,5 @@ int main( argc, argv )
     exit( 0 );                       /* end of procedure                     */
 }
 
+
+
diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c
index 438fe058..2b39e4ec 100644
--- a/editor/specialty/pnmindex.c
+++ b/editor/specialty/pnmindex.c
@@ -1,5 +1,5 @@
 /*============================================================================
-                              pnmindex   
+                                pnmindex
 ==============================================================================
 
   build a visual index of a bunch of PNM images
@@ -32,7 +32,7 @@
 #include "nstring.h"
 #include "pnm.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -56,11 +56,11 @@ systemf(const char * const fmt,
         ...) {
 
     va_list varargs;
-    
+
     size_t dryRunLen;
-    
+
     va_start(varargs, fmt);
-    
+
     pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen);
 
     va_end(varargs);
@@ -83,7 +83,7 @@ systemf(const char * const fmt,
             va_start(varargs, fmt);
 
             pm_vsnprintf(shellCommand, allocSize, fmt, varargs, &realLen);
-                
+
             assert(realLen == dryRunLen);
             va_end(varargs);
 
@@ -94,12 +94,12 @@ systemf(const char * const fmt,
             if (rc != 0)
                 pm_error("shell command '%s' failed.  rc %d",
                          shellCommand, rc);
-            
+
             pm_strfree(shellCommand);
         }
     }
 }
-        
+
 
 
 static const char *
@@ -168,8 +168,8 @@ shellQuote(const char * const arg) {
 
 
 static void
-parseCommandLine(int argc, char ** argv, 
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 
     unsigned int option_def_index;
     optEntry *option_def;
@@ -183,13 +183,13 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "black",       OPT_FLAG,   NULL,                  
+    OPTENT3(0, "black",       OPT_FLAG,   NULL,
             &cmdlineP->black,         0);
-    OPTENT3(0, "noquant",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "noquant",     OPT_FLAG,   NULL,
             &cmdlineP->noquant,       0);
-    OPTENT3(0, "quant",       OPT_FLAG,   NULL,                  
+    OPTENT3(0, "quant",       OPT_FLAG,   NULL,
             &quant,                   0);
-    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,
             &cmdlineP->verbose,       0);
     OPTENT3(0, "size",        OPT_UINT,   &cmdlineP->size,
             &sizeSpec,                0);
@@ -202,7 +202,7 @@ parseCommandLine(int argc, char ** argv,
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE; 
+    opt.allowNegNum = FALSE;
 
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
@@ -212,7 +212,7 @@ parseCommandLine(int argc, char ** argv,
 
     if (!colorsSpec)
         cmdlineP->colors = 256;
-    
+
     if (!sizeSpec)
         cmdlineP->size = 100;
 
@@ -246,7 +246,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-freeCmdline(struct cmdlineInfo const cmdline) {
+freeCmdline(struct CmdlineInfo const cmdline) {
 
     unsigned int i;
 
@@ -326,9 +326,9 @@ rowFileName(const char * const dirName,
             unsigned int const row) {
 
     const char * fileName;
-    
+
     pm_asprintf(&fileName, "%s/pi.%u", dirName, row);
-    
+
     return fileName;
 }
 
@@ -368,7 +368,7 @@ copyImage(const char * const inputFileName,
     systemf("cat %s > %s", inputFileNmToken, outputFileName);
 
     pm_strfree(inputFileNmToken);
-} 
+}
 
 
 
@@ -386,26 +386,26 @@ copyScaleQuantImage(const char * const inputFileName,
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PBM_TYPE:
-        pm_asprintf(&scaleCommand, 
+        pm_asprintf(&scaleCommand,
                     "pamscale -quiet -xysize %u %u %s "
                     "| pgmtopbm > %s",
                     size, size, inputFileNmToken, outputFileName);
         break;
-        
+
     case PGM_TYPE:
-        pm_asprintf(&scaleCommand, 
+        pm_asprintf(&scaleCommand,
                     "pamscale -quiet -xysize %u %u %s >%s",
                     size, size, inputFileNmToken, outputFileName);
         break;
-        
+
     case PPM_TYPE:
         if (quant)
-            pm_asprintf(&scaleCommand, 
+            pm_asprintf(&scaleCommand,
                         "pamscale -quiet -xysize %u %u %s "
                         "| pnmquant -quiet %u > %s",
                         size, size, inputFileNmToken, colors, outputFileName);
         else
-            pm_asprintf(&scaleCommand, 
+            pm_asprintf(&scaleCommand,
                         "pamscale -quiet -xysize %u %u %s >%s",
                         size, size, inputFileNmToken, outputFileName);
         break;
@@ -426,7 +426,7 @@ formatTypeMax(int const typeA,
               int const typeB) {
 
     if (typeA == PPM_TYPE || typeB == PPM_TYPE)
-        return PPM_TYPE; 
+        return PPM_TYPE;
     else if (typeA == PGM_TYPE || typeB == PGM_TYPE)
         return PGM_TYPE;
     else
@@ -441,9 +441,9 @@ thumbnailFileName(const char * const dirName,
                   unsigned int const col) {
 
     const char * fileName;
-    
+
     pm_asprintf(&fileName, "%s/pi.%u.%u", dirName, row, col);
-    
+
     return fileName;
 }
 
@@ -464,7 +464,7 @@ thumbnailFileList(const char * const dirName,
         pm_error("Unable to allocate %u bytes for file list", maxListSize);
 
     list[0] = '\0';
-    
+
     for (col = 0; col < cols; ++col) {
         const char * const fileName = thumbnailFileName(dirName, row, col);
 
@@ -487,19 +487,28 @@ makeImageFile(const char * const thumbnailFileName,
               const char * const inputFileName,
               bool         const blackBackground,
               const char * const outputFileName) {
+/*----------------------------------------------------------------------------
+   Create one thumbnail image.  It consists of the image in the file named
+   'thumbnailFileName' with text of that name appended to the bottom.
 
+   Write the result to the file named 'outputFileName'.
+
+   'blackBackground' means give the image a black background where padding
+   is necessary and make the text white on black.  If false, give the image
+   a white background instead.
+-----------------------------------------------------------------------------*/
     const char * const blackWhiteOpt = blackBackground ? "-black" : "-white";
     const char * const invertStage   = blackBackground ? "| pnminvert " : "";
     const char * inputFileNmToken    = shellQuote(inputFileName);
 
     systemf("pbmtext %s %s"
-            "| pnmcat %s -topbottom %s - "
+            "| pamcat %s -topbottom %s - "
             "> %s",
             inputFileNmToken, invertStage, blackWhiteOpt,
             thumbnailFileName, outputFileName);
 
     pm_strfree(inputFileNmToken);
-}    
+}
 
 
 
@@ -519,21 +528,21 @@ makeThumbnail(const char *  const inputFileName,
     xelval maxval;
     const char * tmpfile;
     const char * fileName;
-        
+
     ifP = pm_openr(inputFileName);
     pnm_readpnminit(ifP, &imageCols, &imageRows, &maxval, &format);
     pm_close(ifP);
-    
+
     pm_asprintf(&tmpfile, "%s/pi.tmp", tempDir);
 
     if (imageCols < size && imageRows < size)
         copyImage(inputFileName, tmpfile);
     else
-        copyScaleQuantImage(inputFileName, tmpfile, format, 
+        copyScaleQuantImage(inputFileName, tmpfile, format,
                             size, quant, colors);
 
     fileName = thumbnailFileName(tempDir, row, col);
-        
+
     makeImageFile(tmpfile, inputFileName, black, fileName);
 
     unlink(tmpfile);
@@ -543,7 +552,7 @@ makeThumbnail(const char *  const inputFileName,
 
     *formatP = format;
 }
-        
+
 
 
 static void
@@ -552,7 +561,7 @@ unlinkThumbnailFiles(const char * const dirName,
                      unsigned int const cols) {
 
     unsigned int col;
-    
+
     for (col = 0; col < cols; ++col) {
         const char * const fileName = thumbnailFileName(dirName, row, col);
 
@@ -569,7 +578,7 @@ unlinkRowFiles(const char * const dirName,
                unsigned int const rows) {
 
     unsigned int row;
-    
+
     for (row = 0; row < rows; ++row) {
         const char * const fileName = rowFileName(dirName, row);
 
@@ -595,7 +604,7 @@ combineIntoRowAndDelete(unsigned int const row,
     const char * fileName;
     const char * quantStage;
     const char * fileList;
-    
+
     fileName = rowFileName(tempDir, row);
 
     unlink(fileName);
@@ -607,7 +616,7 @@ combineIntoRowAndDelete(unsigned int const row,
 
     fileList = thumbnailFileList(tempDir, row, cols);
 
-    systemf("pnmcat %s -leftright -jbottom %s "
+    systemf("pamcat %s -leftright -jbottom %s "
             "%s"
             ">%s",
             blackWhiteOpt, fileList, quantStage, fileName);
@@ -641,7 +650,7 @@ rowFileList(const char * const dirName,
 
         if (strlen(list) + strlen(fileName) + 1 > maxListSize - 1)
             pm_error("File name list too long for this program to handle.");
-        
+
         else {
             strcat(list, " ");
             strcat(list, fileName);
@@ -666,7 +675,7 @@ writeRowsAndDelete(unsigned int const rows,
 
     const char * quantStage;
     const char * fileList;
-    
+
     if (maxFormatType == PPM_TYPE && quant)
         pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colors);
     else
@@ -674,7 +683,7 @@ writeRowsAndDelete(unsigned int const rows,
 
     fileList = rowFileList(tempDir, rows);
 
-    systemf("pnmcat %s -topbottom %s %s",
+    systemf("pamcat %s -topbottom %s %s",
             blackWhiteOpt, fileList, quantStage);
 
     pm_strfree(fileList);
@@ -687,7 +696,7 @@ writeRowsAndDelete(unsigned int const rows,
 
 int
 main(int argc, char *argv[]) {
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     const char * tempDir;
     int maxFormatType;
     unsigned int colsInRow;
@@ -699,7 +708,7 @@ main(int argc, char *argv[]) {
     parseCommandLine(argc, argv, &cmdline);
 
     verbose = cmdline.verbose;
-    
+
     makeTempDir(&tempDir);
 
     maxFormatType = PBM_TYPE;
@@ -714,7 +723,7 @@ main(int argc, char *argv[]) {
 
         int format;
 
-        makeThumbnail(inputFileName, cmdline.size, cmdline.black, 
+        makeThumbnail(inputFileName, cmdline.size, cmdline.black,
                       !cmdline.noquant, cmdline.colors, tempDir,
                       rowsDone, colsInRow, &format);
 
@@ -742,3 +751,6 @@ main(int argc, char *argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/editor/specialty/ppmshift.c b/editor/specialty/ppmshift.c
index a765daa5..27cbb78c3 100644
--- a/editor/specialty/ppmshift.c
+++ b/editor/specialty/ppmshift.c
@@ -9,124 +9,183 @@
 /* V1.1    16.11.1993  Rewritten to be NetPBM.programming conforming */
 /*********************************************************************/
 
+#include <stdbool.h>
+
+#include "mallocvar.h"
+#include "rand.h"
+#include "shhopt.h"
 #include "ppm.h"
 
-/**************************/
-/* start of main function */
-/**************************/
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+
+    unsigned int shift;
+    unsigned int seedSpec;
+    unsigned int seed;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = true;  /* We have no parms that are negative numbers */
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "seed",            OPT_UINT,     &cmdlineP->seed,
+            &cmdlineP->seedSpec,         0);
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1)
+        pm_error("You must specify the shift factor as an argument");
+    else {
+        int const arg1 = atoi(argv[1]);
+        if (arg1 < 0)
+            pm_error("shift factor must be 0 or more");
+        cmdlineP->shift = arg1;
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments (%u).  "
+                         "Shift factor and input file name are the only "
+                         "possible arguments", argc-1);
+        }
+    }
+    free(option_def);
+}
+
+
+
+static void
+shiftRow(pixel *            const srcrow,
+         unsigned int       const cols,
+         unsigned int       const shift,
+         pixel *            const destrow,
+         struct pm_randSt * const randStP) {
+
+    /* the range by which a line is shifted lays in the range from */
+    /* -shift/2 .. +shift/2 pixels; however, within this range it is */
+    /* randomly chosen */
+
+    pixel * pP;
+    pixel * pP2;
+    int nowshift;
+
+    if (shift != 0)
+        nowshift = (pm_rand(randStP) % (shift+1)) - ((shift+1) / 2);
+    else
+        nowshift = 0;
+
+    pP  = &srcrow[0];
+    pP2 = &destrow[0];
+
+    /* if the shift value is less than zero, we take the original
+       pixel line and copy it into the destination line translated
+       to the left by x pixels. The empty pixels on the right end
+       of the destination line are filled up with the pixel that
+       is the right-most in the original pixel line.
+    */
+    if (nowshift < 0) {
+        unsigned int col;
+        pP += abs(nowshift);
+        for (col = 0; col < cols; ++col) {
+            PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
+            ++pP2;
+            if (col < (cols + nowshift) - 1)
+                ++pP;
+        }
+    } else {
+        unsigned int col;
+        /* The shift value is 0 or positive, so fill the first
+           <nowshift> pixels of the destination line with the
+           first pixel from the source line, and copy the rest of
+           the source line to the dest line
+        */
+        for (col = 0; col < cols; ++col) {
+            PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
+            ++pP2;
+            if (col >= nowshift)
+                ++pP;
+        }
+    }
+}
+
+
+
 int
-main(int    argc,
-     char * argv[]) {
+main(int argc, const char ** argv) {
 
     FILE * ifP;
-    unsigned int row;
-    int argn, rows, cols, format;
+    struct CmdlineInfo cmdline;
+    int rows, cols, format;
+    pixval maxval;
     pixel * srcrow;
     pixel * destrow;
-    pixval maxval;
-    int shift, nowshift;
-    int shiftArg;
-
-    const char * const usage = "shift [ppmfile]\n        shift: maximum number of pixels to shift a line by\n";
+    unsigned int row;
+    unsigned int shift;
+    struct pm_randSt randSt;
 
     /* parse in 'default' parameters */
-    ppm_init(&argc, argv);
-
-    argn = 1;
-
-    /* parse in shift number */
-    if (argn == argc)
-        pm_usage(usage);
-    if (sscanf(argv[argn], "%d", &shiftArg) != 1)
-        pm_usage(usage);
-    if (shiftArg < 0)
-        pm_error("shift factor must be 0 or more");
-    ++argn;
-
-    /* parse in filename (if present, stdin otherwise) */
-    if (argn != argc)
-    {
-        ifP = pm_openr(argv[argn]);
-        ++argn;
-    }
-    else
-        ifP = stdin;
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    pm_randinit(&randSt);
+    pm_srand2(&randSt, cmdline.seedSpec, cmdline.seed);
 
-    if (argn != argc)
-        pm_usage(usage);
+    ifP = pm_openr(cmdline.inputFileName);
 
     /* read first data from file */
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
-    if (shiftArg > cols) {
+    if (cmdline.shift > cols) {
         shift = cols;
-        pm_message("shift amount is larger than picture width - reset to %d",
+        pm_message("shift amount is larger than picture width - reset to %u",
                    shift);
     } else
-        shift = shiftArg;
-
-    srcrow = ppm_allocrow(cols);
+        shift = cmdline.shift;
 
+    srcrow  = ppm_allocrow(cols);
     destrow = ppm_allocrow(cols);
 
     ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
-    srand(pm_randseed());
-
-    /** now do the shifting **/
-    /* the range by which a line is shifted lays in the range from */
-    /* -shift/2 .. +shift/2 pixels; however, within this range it is */
-    /* randomly chosen */
     for (row = 0; row < rows; ++row) {
-        pixel * pP;
-        pixel * pP2;
-
-        if (shift != 0)
-            nowshift = (rand() % (shift+1)) - ((shift+1) / 2);
-        else
-            nowshift = 0;
-
         ppm_readppmrow(ifP, srcrow, cols, maxval, format);
 
-        pP  = &srcrow[0];
-        pP2 = &destrow[0];
-
-        /* if the shift value is less than zero, we take the original
-           pixel line and copy it into the destination line translated
-           to the left by x pixels. The empty pixels on the right end
-           of the destination line are filled up with the pixel that
-           is the right-most in the original pixel line.
-        */
-        if (nowshift < 0) {
-            unsigned int col;
-            pP += abs(nowshift);
-            for (col = 0; col < cols; ++col) {
-                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-                ++pP2;
-                if (col < (cols + nowshift) - 1)
-                    ++pP;
-            }
-        } else {
-            unsigned int col;
-            /* The shift value is 0 or positive, so fill the first
-               <nowshift> pixels of the destination line with the
-               first pixel from the source line, and copy the rest of
-               the source line to the dest line
-            */
-            for (col = 0; col < cols; ++col) {
-                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-                ++pP2;
-                if (col >= nowshift)
-                    ++pP;
-            }
-        }
+        shiftRow(srcrow, cols, shift, destrow, &randSt);
 
         ppm_writeppmrow(stdout, destrow, cols, maxval, 0);
     }
 
-    pm_close(ifP);
-    ppm_freerow(srcrow);
     ppm_freerow(destrow);
+    ppm_freerow(srcrow);
+    pm_close(ifP);
+    pm_randterm(&randSt);
 
     return 0;
 }
diff --git a/editor/specialty/ppmspread.c b/editor/specialty/ppmspread.c
index 6753f4fe..7b9558e3 100644
--- a/editor/specialty/ppmspread.c
+++ b/editor/specialty/ppmspread.c
@@ -10,102 +10,158 @@
 
 #include <string.h>
 
+#include "nstring.h"
+#include "rand.h"
+#include "shhopt.h"
 #include "ppm.h"
 
 
+struct CmdlineInfo {
+    /* This structure represents all of the information the user
+       supplied in the command line but in a form that's easy for the
+       program to use.
+    */
+    const char * inputFilename;  /* '-' if stdin */
+    unsigned int spread;
+    unsigned int randomseedSpec;
+    unsigned int randomseed;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP ) {
+
+    optEntry     * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3     opt;
+    unsigned int   option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    option_def_index = 0;          /* Incremented by OPTENTRY */
+
+    OPTENT3(0, "randomseed", OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum = 1;
+
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+
+    if (argc-1 < 1)
+        pm_error("You must specify the spread factor as an argument");
+    else {
+        const char * error;
+        pm_string_to_uint(argv[1], &cmdlineP->spread, &error);
+
+        if (error)
+            pm_error("Spread factor '%s' is not an unsigned integer.  %s",
+                     argv[1], error);
+
+        if (argc-1 < 2)
+            cmdlineP->inputFilename = "-";
+        else {
+            cmdlineP->inputFilename = argv[2];
+            if (argc-1 >2)
+                pm_error("Too many arguments: %u.  "
+                         "The only possible arguments are "
+                         "the spread factor and the optional input file name",
+                         argc-1);
+        }
+    }
+}
+
+
+
+static void
+spreadRow(pixel **           const srcarray,
+          unsigned int       const cols,
+          unsigned int       const rows,
+          unsigned int       const spread,
+          unsigned int       const row,
+          pixel **           const destarray,
+          struct pm_randSt * const randStP) {
+
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        pixel const p = srcarray[row][col];
+
+        int const xdis = (pm_rand(randStP) % (spread + 1) )
+            - ((spread + 1) / 2);
+        int const ydis = (pm_rand(randStP) % (spread + 1))
+            - ((spread + 1) / 2);
+
+        int const xnew = col + xdis;
+        int const ynew = row + ydis;
+
+        /* only set the displaced pixel if it's within the bounds
+           of the image
+        */
+        if (xnew >= 0 && xnew < cols && ynew >= 0 && ynew < rows) {
+            /* Displacing a pixel is accomplished by swapping it
+               with another pixel in its vicinity.
+            */
+            pixel const p2 = srcarray[ynew][xnew];
+                /* Original value of second pixel */
+
+            /* Set second pixel to new value */
+            PPM_ASSIGN(destarray[ynew][xnew],
+                       PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+
+            /* Set first pixel to (old) value of second */
+            PPM_ASSIGN(destarray[row][col],
+                       PPM_GETR(p2), PPM_GETG(p2), PPM_GETB(p2));
+        } else {
+            /* Displaced pixel is out of bounds; leave the old pixel there.
+            */
+            PPM_ASSIGN(destarray[row][col],
+                       PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+        }
+    }
+}
+
+
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int          argc,
+     const char * argv[]) {
 
+    struct CmdlineInfo cmdline;
     FILE * ifP;
-    int argn, rows, cols;
+    int rows, cols;
     unsigned int row;
-    pixel ** destarray, ** srcarray;
-    pixel * pP;
-    pixel * pP2;
+    pixel ** destarray;
+    pixel ** srcarray;
     pixval maxval;
-    pixval r1, g1, b1;
-    int amount;
-    const char * const usage = "amount [ppmfile]\n        amount: # of pixels to displace a pixel by at most\n";
-
-    /* parse in 'default' parameters */
-    ppm_init(&argc, argv);
-
-    argn = 1;
-
-    /* parse in amount & seed */
-    if (argn == argc)
-        pm_usage(usage);
-    if (sscanf(argv[argn], "%d", &amount) != 1)
-        pm_usage(usage);
-    if (amount < 0)
-        pm_error("amount should be a positive number");
-    ++argn;
-
-    /* parse in filename (if present, stdin otherwise) */
-    if (argn != argc)
-    {
-        ifP = pm_openr(argv[argn]);
-        ++argn;
-    }
-    else
-        ifP = stdin;
+    struct pm_randSt randSt;
 
-    if (argn != argc)
-        pm_usage(usage);
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
 
     srcarray = ppm_readppm(ifP, &cols, &rows, &maxval);
 
     destarray = ppm_allocarray(cols, rows);
 
+    pm_randinit(&randSt);
+    pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed);
+
     /* clear out the buffer */
     for (row = 0; row < rows; ++row)
         memset(destarray[row], 0, cols * sizeof(pixel));
 
-    srand(pm_randseed());
-
-    /* start displacing pixels */
+    /* Displace pixels */
     for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        pP = &srcarray[row][0];
-
-        for (col = 0; col < cols; ++col) {
-            int const xdis = (rand() % (amount+1)) - ((amount+1) / 2);
-            int const ydis = (rand() % (amount+1)) - ((amount+1) / 2);
+        spreadRow(srcarray, cols, rows, cmdline.spread, row,
+                  destarray, &randSt);
 
-            int const xnew = col + xdis;
-            int const ynew = row + ydis;
-
-            /* only set the displaced pixel if it's within the bounds
-               of the image
-            */
-            if (xnew >= 0 && xnew < cols && ynew >= 0 && ynew < rows) {
-                /* displacing a pixel is accomplished by swapping it
-                   with another pixel in its vicinity - so, first
-                   store other pixel's RGB
-                */
-                pP2 = &srcarray[ynew][xnew];
-                r1 = PPM_GETR(*pP2);
-                g1 = PPM_GETG(*pP2);
-                b1 = PPM_GETB(*pP2);
-                /* set second pixel to new value */
-                pP2 = &destarray[ynew][xnew];
-                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-                
-                /* now, set first pixel to (old) value of second */
-                pP2 = &destarray[row][col];
-                PPM_ASSIGN(*pP2, r1, g1, b1);
-            } else {
-                /* displaced pixel is out of bounds; leave the old
-                   pixel there
-                */
-                pP2 = &destarray[row][col];
-                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-            }
-            ++pP;
-        }
     }
+    pm_randterm(&randSt);
 
     ppm_writeppm(stdout, destarray, cols, rows, maxval, 0);
 
@@ -115,3 +171,5 @@ main(int    argc,
 
     return 0;
 }
+
+