about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2018-03-25 17:14:57 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2018-03-25 17:14:57 +0000
commitcd86a2c8d798c98b2f5d7656971fc4553b4ed172 (patch)
tree6218a52f6e09f0cb0ff15c0a0b130da920328e8e /editor
parente488b82f0f446576138084a1bd57b7b4406e8db0 (diff)
downloadnetpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.tar.gz
netpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.tar.xz
netpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.zip
Copy Development as new Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3186 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r--editor/pnmconvol.c130
-rwxr-xr-xeditor/pnmquant20
-rw-r--r--editor/pnmremap.c250
3 files changed, 227 insertions, 173 deletions
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index d1feb0a3..98ec3a6b 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -169,7 +169,7 @@ getMatrixOptDimensions(const char *   const matrixOptString,
                 if (colCt != *widthP)
                 pm_error("-matrix option value contains rows of different "
                          "widths: %u and %u", *widthP, colCt);
-            }            
+            }
             pm_strfree(rowString);
             cursor = next;
 
@@ -211,7 +211,7 @@ parseMatrixRow(const char * const matrixOptRowString,
                 char * trailingJunk;
                 weight[col] = strtod(colString, &trailingJunk);
 
-                if (*trailingJunk != '\0') 
+                if (*trailingJunk != '\0')
                     pm_error("The Column %u element of the row '%s' in the "
                              "-matrix value is not a valid floating point "
                              "number", col, matrixOptRowString);
@@ -235,7 +235,7 @@ parseMatrixOptWithDimensions(const char * const matrixOptString,
                              unsigned int const width,
                              unsigned int const height,
                              float **     const weight) {
-    
+
     unsigned int row;
     const char * cursor;
 
@@ -262,7 +262,7 @@ parseMatrixOptWithDimensions(const char * const matrixOptString,
             }
         }
     }
-}    
+}
 
 
 
@@ -311,7 +311,7 @@ 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.  
+   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.
@@ -336,9 +336,9 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->matrixSpec,     0)
     OPTENT3(0, "matrixfile",   OPT_STRINGLIST, &cmdlineP->matrixfile,
             &matrixfileSpec,           0)
-    OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,
             &cmdlineP->nooffset,       0);
-    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
+    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,
             &cmdlineP->normalize,      0);
     OPTENT3(0, "bias",         OPT_UINT,   &cmdlineP->bias,
             &biasSpec,                 0);
@@ -382,7 +382,7 @@ parseCommandLine(int argc, char ** argv,
                      "argument is the input file name");
     } else {
         /* It's an old style invocation we accept for backward compatibility */
-        
+
         if (argc-1 < 1)
             pm_error("You must specify either -matrix or -matrixfile "
                      "at least one argument which names an old-style PGM "
@@ -394,7 +394,7 @@ parseCommandLine(int argc, char ** argv,
                 cmdlineP->inputFileName = argv[2];
             else
                 cmdlineP->inputFileName = "-";
-            
+
             if (argc-1 > 2)
                 pm_error("Too many arguments.  Only acceptable arguments are: "
                          "convolution matrix file name and input file name");
@@ -416,11 +416,11 @@ struct ConvKernel {
     float ** weight[3];
         /* weight[PLANE][ROW][COL] is the weight to give to Plane PLANE
            of the pixel at row ROW, column COL within the convolution window.
-           
+
            One means full weight.
 
            It can have magnitude greater than or less than one.  It can be
-           positive or negative.  
+           positive or negative.
         */
     unsigned int bias;
         /* The amount to be added to the linear combination of sample values.
@@ -441,7 +441,7 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
 
     for (plane = 0; plane < convKernelP->planes; ++plane)
         sum[plane] = 0.0; /* initial value */
-    
+
     for (row = 0; row < convKernelP->rows; ++row) {
         unsigned int col;
         for (col = 0; col < convKernelP->cols; ++col) {
@@ -462,9 +462,9 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
             if (sum[plane] < 0.0)
                 negative = true;
         }
-    
+
         if (biased) {
-            pm_message("WARNING - this convolution matrix is biased.  " 
+            pm_message("WARNING - this convolution matrix is biased.  "
                        "red, green, and blue average weights: %f, %f, %f "
                        "(unbiased would be 1).",
                        sum[PAM_RED_PLANE],
@@ -488,7 +488,7 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
 
 static void
 convKernelCreatePnm(struct pam *         const cpamP,
-                    tuple * const *      const ctuples, 
+                    tuple * const *      const ctuples,
                     unsigned int         const depth,
                     bool                 const offsetPnm,
                     struct ConvKernel ** const convKernelPP) {
@@ -526,7 +526,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
         unsigned int row;
 
         MALLOCARRAY_NOFAIL(convKernelP->weight[plane], cpamP->height);
-    
+
         for (row = 0; row < cpamP->height; ++row) {
             unsigned int col;
 
@@ -578,9 +578,9 @@ normalizeKernelPlane(struct ConvKernel * const convKernelP,
 
     for (row = 0, sum = 0.0; row < convKernelP->rows; ++row) {
         unsigned int col;
-            
+
         for (col = 0; col < convKernelP->cols; ++col) {
-                
+
             sum += convKernelP->weight[plane][row][col];
         }
     }
@@ -592,7 +592,7 @@ normalizeKernelPlane(struct ConvKernel * const convKernelP,
 
         for (row = 0; row < convKernelP->rows; ++row) {
             unsigned int col;
-                
+
             for (col = 0; col < convKernelP->cols; ++col)
                 convKernelP->weight[plane][row][col] *= scaler;
         }
@@ -641,7 +641,7 @@ getKernelPnm(const char *         const fileName,
     /* Read in the convolution matrix. */
     ctuples = pnm_readpam(cifP, &cpam, PAM_STRUCT_SIZE(tuple_type));
     pm_close(cifP);
-    
+
     validateKernelDimensions(cpam.width, cpam.height);
 
     convKernelCreatePnm(&cpam, ctuples, depth, offset, convKernelPP);
@@ -657,7 +657,7 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
 /*----------------------------------------------------------------------------
    Create a convolution kernel as described by a -matrix command line
    option.
-   
+
    The option value is 'matrixOpt'.
 -----------------------------------------------------------------------------*/
     struct ConvKernel * convKernelP;
@@ -678,7 +678,7 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
 
             MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row],
                                matrixOpt.width);
-    
+
             for (col = 0; col < matrixOpt.width; ++col)
                 convKernelP->weight[plane][row][col] =
                     matrixOpt.weight[row][col];
@@ -728,7 +728,7 @@ parsePlaneFileLine(const char *   const line,
         else {
             char * trailingJunk;
             weight[colCt] = strtod(token, &trailingJunk);
-            if (*trailingJunk != '\0') 
+            if (*trailingJunk != '\0')
                 pm_error("The Column %u element of the row '%s' in the "
                          "-matrix value is not a valid floating point "
                          "number", colCt, line);
@@ -744,7 +744,7 @@ parsePlaneFileLine(const char *   const line,
 
 
 static void
-readPlaneFile(FILE *         const ifP, 
+readPlaneFile(FILE *         const ifP,
               float ***      const weightP,
               unsigned int * const widthP,
               unsigned int * const heightP) {
@@ -783,7 +783,7 @@ readPlaneFile(FILE *         const ifP,
                 eof = true;
             else {
                 REALLOCARRAY(weight, rowCt + 1);
-            
+
                 if (weight == NULL)
                     pm_error("Unable to allocate memory for "
                              "convolution matrix");
@@ -799,7 +799,7 @@ readPlaneFile(FILE *         const ifP,
                             pm_error("Multiple row widths in the convolution "
                                      "matrix file: %u columns and %u columns.",
                                      width, thisWidth);
-                    }                    
+                    }
                     ++rowCt;
                 }
                 pm_strfree(line);
@@ -818,7 +818,7 @@ readPlaneFile(FILE *         const ifP,
 static void
 copyWeight(float **       const srcWeight,
            unsigned int   const width,
-           unsigned int   const height, 
+           unsigned int   const height,
            float ***      const dstWeightP) {
 /*----------------------------------------------------------------------------
    Make a copy, in dynamically allocated memory, of the weight matrix
@@ -832,7 +832,7 @@ copyWeight(float **       const srcWeight,
 
     if (dstWeight == NULL)
         pm_error("Could not allocate memory for convolution matrix");
-   
+
     for (row = 0; row < height; ++row) {
         unsigned int col;
 
@@ -973,7 +973,7 @@ validateEnoughImageToConvolve(const struct pam *        const inpamP,
         pm_error("Image is too short (%u rows) to convolve with this "
                  "%u-row convolution kernel.",
                  inpamP->height, convKernelP->rows);
-    
+
     if (inpamP->width < convKernelP->cols + 1)
         pm_error("Image is too narrow (%u columns) to convolve with this "
                  "%u-column convolution kernel.",
@@ -994,7 +994,7 @@ allocRowbuf(struct pam * const pamP,
         pm_error("Failed to allocate %u-row buffer", height);
     else {
         unsigned int row;
-    
+
         for (row = 0; row < height; ++row)
             rowbuf[row] = pnm_allocpamrow(pamP);
     }
@@ -1043,7 +1043,7 @@ readAndScaleRows(struct pam *              const inpamP,
                  unsigned int              const outputDepth) {
 /*----------------------------------------------------------------------------
   Read in 'count' rows into rowbuf[].
-  
+
   Scale the contents to maxval 'outputMaxval' and expand to depth
   'outputDepth'.
 -----------------------------------------------------------------------------*/
@@ -1173,7 +1173,7 @@ convolveGeneralRowPlane(struct pam *              const pamP,
     unsigned int const ccolso2 = convKernelP->cols / 2;
 
     unsigned int col;
-    
+
     for (col = 0; col < pamP->width; ++col) {
         if (col < ccolso2 || col >= pamP->width - ccolso2)
             /* The unconvolved left or right edge */
@@ -1283,7 +1283,7 @@ allocSum(unsigned int const depth,
 
         for (plane = 0; plane < depth; ++plane) {
             MALLOCARRAY(sum[plane], size);
-            
+
             if (!sum[plane])
                 pm_error("Could not allocate memory for %u sums", size);
         }
@@ -1330,7 +1330,7 @@ computeInitialColumnSums(struct pam *              const pamP,
                  row < convKernelP->rows;
                  ++row)
                 convColumnSum[plane][col] += window[row][col][plane];
-        }            
+        }
     }
 }
 
@@ -1358,7 +1358,7 @@ convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
   be convolved because the convolution window runs off the edge).
 -----------------------------------------------------------------------------*/
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         unsigned int const crowso2 = convKernelP->rows / 2;
         unsigned int const ccolso2 = convKernelP->cols / 2;
@@ -1385,7 +1385,7 @@ convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
                 } else {
                     /* Column numbers to subtract or add to isum */
                     unsigned int const subcol = col - ccolso2 - 1;
-                    unsigned int const addcol = col + ccolso2;  
+                    unsigned int const addcol = col + ccolso2;
 
                     gisum -= convColumnSum[plane][subcol];
                     gisum += convColumnSum[plane][addcol];
@@ -1429,7 +1429,7 @@ convolveRowWithColumnSumsVertical(
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         unsigned int col;
-    
+
         for (col = 0; col < pamP->width; ++col) {
             if (col < ccolso2 || col >= pamP->width - ccolso2) {
                 /* The unconvolved left or right edge */
@@ -1513,12 +1513,12 @@ convolveMeanRowPlane(struct pam *              const pamP,
             } else {
                 /* Column numbers to subtract or add to isum */
                 unsigned int const subcol = col - ccolso2 - 1;
-                unsigned int const addcol = col + ccolso2;  
-                
+                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] =
@@ -1674,7 +1674,7 @@ allocRowSum(unsigned int const depth,
 
         for (plane = 0; plane < depth; ++plane) {
             MALLOCARRAY(sum[plane], height);
-            
+
             if (!sum[plane])
                 pm_error("Could not allocate memory for %u rows of sums",
                          height);
@@ -1683,7 +1683,7 @@ allocRowSum(unsigned int const depth,
 
                 for (row = 0; row < height; ++row) {
                     MALLOCARRAY(sum[plane][row], width);
-                    
+
                     if (!sum[plane][row])
                         pm_error("Could not allocate memory "
                                  "for a row of sums");
@@ -1747,7 +1747,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                    starts at the left edge of the image.
                 */
                 unsigned int const leftcol = 0;
-            
+
                 unsigned int crow;
 
                 for (crow = 0, matrixSum = 0.0;
@@ -1756,7 +1756,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                     tuple * const tuplesInWindow = &window[crow][leftcol];
 
                     unsigned int ccol;
-                
+
                     sumWindow[crow][col] = 0;
                     for (ccol = 0; ccol < convKernelP->cols; ++ccol)
                         sumWindow[crow][col] += tuplesInWindow[ccol][plane];
@@ -1769,7 +1769,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                 unsigned int const addcol  = col + ccolso2;
 
                 unsigned int crow;
-                
+
                 for (crow = 0, matrixSum = 0.0;
                      crow < convKernelP->rows;
                      ++crow) {
@@ -1798,7 +1798,7 @@ setupCircMap2(tuple **     const rowbuf,
               unsigned int const windowHeight) {
 
     unsigned int const toprow = windowTopRow % windowHeight;
-    
+
     unsigned int crow;
     unsigned int i;
 
@@ -1874,7 +1874,7 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
             }
         } else {
             unsigned int const subcol  = col - ccolso2 - 1;
-            unsigned int const addcol  = col + ccolso2;  
+            unsigned int const addcol  = col + ccolso2;
 
             unsigned int crow;
 
@@ -1947,7 +1947,7 @@ convolveHorizontal(struct pam *              const inpamP,
 
         for (crow = 0; crow < convKernelP->rows; ++crow)
             sumCircMap[crow] = convRowSum[plane][crow];
- 
+
         convolveHorizontalRowPlane0(outpamP, circMap, convKernelP, plane,
                                     outputrow, sumCircMap);
     }
@@ -1969,13 +1969,13 @@ convolveHorizontal(struct pam *              const inpamP,
 
             readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
                             outpamP->maxval, outpamP->depth);
-            
+
             setupCircMap2(rowbuf, convRowSum[plane], circMap, sumCircMap,
                           windowTopRow, windowHeight);
 
             convolveHorizontalRowPlane(outpamP, circMap, convKernelP, plane,
                                        outputrow, sumCircMap);
-            
+
             pnm_writepamrow(outpamP, outputrow);
         }
     }
@@ -2005,7 +2005,7 @@ convolveVerticalRowPlane(struct pam *              const pamP,
         */
     unsigned int const addrow = 1 + (convKernelP->rows - 1);
         /* Bottom row of convolution window: What we add to running sum */
-    
+
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
@@ -2304,7 +2304,7 @@ main(int argc, char * argv[]) {
  original) in January 1995.
 
  Reduce run time by general optimizations and handling special cases of
- convolution matrices.  Program automatically determines if convolution 
+ convolution matrices.  Program automatically determines if convolution
  matrix is one of the types it can make use of so no extra command line
  arguments are necessary.
 
@@ -2322,7 +2322,7 @@ main(int argc, char * argv[]) {
  -------------------------------------------
  Created separate functions as code was getting too large to put keep both
  PGM and PPM cases in same function and also because SWITCH statement in
- inner loop can take progressively more time the larger the size of the 
+ inner loop can take progressively more time the larger the size of the
  convolution matrix.  GCC is affected this way.
 
  Removed use of MOD (%) operator from innermost loop by modifying manner in
@@ -2331,50 +2331,50 @@ main(int argc, char * argv[]) {
  This is from the file pnmconvol.README, dated August 1995, extracted in
  April 2000, which was in the March 1994 Netpbm release:
 
- ----------------------------------------------------------------------------- 
+ -----------------------------------------------------------------------------
  This is a faster version of the pnmconvol.c program that comes with netpbm.
  There are no changes to the command line arguments, so this program can be
  dropped in without affecting the way you currently run it.  An updated man
  page is also included.
- 
+
  My original intention was to improve the running time of applying a
  neighborhood averaging convolution matrix to an image by using a different
  algorithm, but I also improved the run time of performing the general
  convolution by optimizing that code.  The general convolution runs in 1/4 to
  1/2 of the original time and neighborhood averaging runs in near constant
  time for the convolution masks I tested (3x3, 5x5, 7x7, 9x9).
- 
+
  Sample times for two computers are below.  Times are in seconds as reported
  by /bin/time for a 512x512 pgm image.
- 
+
  Matrix                  IBM RS6000      SUN IPC
  Size & Type                220
- 
+
  3x3
  original pnmconvol         6.3            18.4
  new general case           3.1             6.0
  new average case           1.8             2.6
- 
+
  5x5
  original pnmconvol        11.9            44.4
  new general case           5.6            11.9
  new average case           1.8             2.6
- 
+
  7x7
  original pnmconvol        20.3            82.9
  new general case           9.4            20.7
  new average case           1.8             2.6
- 
+
  9x9
  original pnmconvol        30.9           132.4
  new general case          14.4            31.8
  new average case           1.8             2.6
- 
- 
+
+
  Send all questions/comments/bugs to me at burns@chem.psu.edu.
- 
+
  - Mike
- 
+
  ----------------------------------------------------------------------------
  Mike Burns                                              System Administrator
  burns@chem.psu.edu                                   Department of Chemistry
diff --git a/editor/pnmquant b/editor/pnmquant
index 35a75e96..0bb328d2 100755
--- a/editor/pnmquant
+++ b/editor/pnmquant
@@ -96,6 +96,8 @@ sub parseCommandLine(@) {
                                   "spreadbrightness",
                                   "spreadluminosity",
                                   "floyd|fs!",
+                                  "norandom",
+                                  "randomseed=i",
                                   "quiet",
                                   "plain");
 
@@ -248,9 +250,10 @@ sub makeColormap($$$$$) {
 
 
 
-sub remap($$$$) {
+sub remap($$$$$$) {
 
-    my ($mapfileSpec, $opt_floyd, $opt_plain, $opt_quiet) = @_;
+    my ($mapfileSpec, $opt_floyd, $opt_norandom, $opt_randomseed,
+        $opt_plain, $opt_quiet) = @_;
 
     # Remap the image on Standard Input to Standard Output, using the colors
     # from the colormap file named $mapfileSpec.
@@ -261,6 +264,17 @@ sub remap($$$$) {
     if ($opt_floyd) {
         push(@options, "-floyd");
     }
+    if ($opt_norandom) {
+        push(@options, "-norandom");
+    }
+    if (defined($opt_randomseed)) {
+        if ($opt_randomseed < 0) {
+             print(STDERR "-randomseed value must not be negative.  " .
+                   "You specified $opt_randomseed\n");
+             exit(10);
+        }
+        push(@options, "-randomseed=$opt_randomseed");
+    }
     if ($opt_plain) {
         push(@options, "-plain");
     }
@@ -311,6 +325,8 @@ open(STDOUT, ">&OLDOUT");
 
 remap($mapfileSpec, 
       $cmdlineR->{floyd}, 
+      $cmdlineR->{norandom}, 
+      $cmdlineR->{randomseed}, 
       $cmdlineR->{plain},
       $cmdlineR->{quiet});
 
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index ed758aa3..59b1e84b 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -41,6 +41,17 @@ enum MissingMethod {
     MISSING_CLOSE
 };
 
+enum InitRandom {
+    RANDOM_NONE,
+    RANDOM_WITHSEED,
+    RANDOM_NOSEED
+};
+
+struct Random {
+    enum InitRandom init;
+    unsigned int seed;
+};
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -48,9 +59,9 @@ struct CmdlineInfo {
     const char * inputFilespec;  /* Filespec of input file */
     const char * mapFilespec;    /* Filespec of colormap file */
     unsigned int floyd;   /* Boolean: -floyd/-fs option */
-    unsigned int norandom;
+    struct Random random;
     enum MissingMethod missingMethod;
-    char * missingcolor;      
+    char * missingcolor;
         /* -missingcolor value.  Null if not specified */
     unsigned int verbose;
 };
@@ -62,7 +73,7 @@ parseCommandLine (int argc, const 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.  
+   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.
@@ -78,28 +89,30 @@ parseCommandLine (int argc, const char ** argv,
     unsigned int option_def_index;
 
     unsigned int nofloyd, firstisdefault;
-    unsigned int missingSpec, mapfileSpec;
+    unsigned int missingSpec, mapfileSpec, norandomSpec, randomseedSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "floyd",          OPT_FLAG,   
+    OPTENT3(0,   "floyd",          OPT_FLAG,
             NULL,                       &cmdlineP->floyd,    0);
-    OPTENT3(0,   "fs",             OPT_FLAG,   
+    OPTENT3(0,   "fs",             OPT_FLAG,
             NULL,                       &cmdlineP->floyd,    0);
-    OPTENT3(0,   "nofloyd",        OPT_FLAG,   
+    OPTENT3(0,   "nofloyd",        OPT_FLAG,
             NULL,                       &nofloyd,            0);
-    OPTENT3(0,   "nofs",           OPT_FLAG,   
+    OPTENT3(0,   "nofs",           OPT_FLAG,
             NULL,                       &nofloyd,            0);
-    OPTENT3(0,   "norandom",       OPT_FLAG,   
-            NULL,                       &cmdlineP->norandom, 0);
-    OPTENT3(0,   "firstisdefault", OPT_FLAG,   
+    OPTENT3(0,   "norandom",       OPT_FLAG,
+            NULL,                       &norandomSpec,       0);
+    OPTENT3(0,   "randomseed",     OPT_UINT,
+            &cmdlineP->random.seed,     &randomseedSpec,     0);
+    OPTENT3(0,   "firstisdefault", OPT_FLAG,
             NULL,                       &firstisdefault,     0);
-    OPTENT3(0,   "mapfile",        OPT_STRING, 
+    OPTENT3(0,   "mapfile",        OPT_STRING,
             &cmdlineP->mapFilespec,    &mapfileSpec,         0);
-    OPTENT3(0,   "missingcolor",   OPT_STRING, 
+    OPTENT3(0,   "missingcolor",   OPT_STRING,
             &cmdlineP->missingcolor,   &missingSpec,         0);
-    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,
             &cmdlineP->verbose,                              0);
 
     opt.opt_table = option_def;
@@ -107,13 +120,32 @@ parseCommandLine (int argc, const char ** argv,
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
     cmdlineP->missingcolor = NULL;  /* default value */
-    
+
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (cmdlineP->floyd && nofloyd)
         pm_error("You cannot specify both -floyd and -nofloyd options.");
 
+    if (cmdlineP->floyd) {
+        if (norandomSpec) {
+            if (randomseedSpec)
+                pm_error("You cannot specify both -norandom and -randomseed.");
+            else
+                cmdlineP->random.init = RANDOM_NONE;
+        } else {
+            if (randomseedSpec)
+                cmdlineP->random.init = RANDOM_WITHSEED;
+            else
+                cmdlineP->random.init = RANDOM_NOSEED;
+        }
+    } else {
+        if (norandomSpec)
+            pm_message("-floyd not specified.  -norandom has no effect.");
+        if (randomseedSpec)
+            pm_message("-floyd not specified.  Ignoring -randomseed value.");
+    }
+
     if (firstisdefault && missingSpec)
         pm_error("You cannot specify both -missing and -firstisdefault.");
 
@@ -176,7 +208,7 @@ grayscaleToDepth3(tuple const tuple) {
 static void
 adjustDepthTuple(tuple           const tuple,
                  depthAdjustment const adjustment) {
-    
+
     switch (adjustment) {
     case ADJUST_NONE:
         break;
@@ -194,7 +226,7 @@ adjustDepthTuple(tuple           const tuple,
 static void
 inverseAdjustDepthTuple(tuple           const tuple,
                         depthAdjustment const adjustment) {
-    
+
     switch (adjustment) {
     case ADJUST_NONE:
         break;
@@ -249,7 +281,7 @@ selectDepthAdjustment(const struct pam * const pamP,
 
    The only depth changes we know how to do are:
 
-     - from tuple type RGB, depth 3 to depth 1 
+     - from tuple type RGB, depth 3 to depth 1
 
        We change it to grayscale or black and white.
 
@@ -296,7 +328,7 @@ selectDepthAdjustment(const struct pam * const pamP,
                      "that I know how to convert to the map depth.  "
                      "I can convert RGB, GRAYSCALE, and BLACKANDWHITE.  "
                      "The input image is '%.*s'.",
-                     newDepth, pamP->depth, 
+                     newDepth, pamP->depth,
                      (int)sizeof(pamP->tuple_type), pamP->tuple_type);
         }
     }
@@ -305,8 +337,8 @@ selectDepthAdjustment(const struct pam * const pamP,
 
 
 static void
-computeColorMapFromMap(struct pam *   const mappamP, 
-                       tuple **       const maptuples, 
+computeColorMapFromMap(struct pam *   const mappamP,
+                       tuple **       const maptuples,
                        tupletable *   const colormapP,
                        unsigned int * const newcolorsP) {
 /*----------------------------------------------------------------------------
@@ -317,12 +349,12 @@ computeColorMapFromMap(struct pam *   const mappamP,
 
    Return the number of colors in the returned colormap as *newcolorsP.
 -----------------------------------------------------------------------------*/
-    unsigned int colors; 
+    unsigned int colors;
 
     if (mappamP->width == 0 || mappamP->height == 0)
         pm_error("colormap file contains no pixels");
 
-    *colormapP = 
+    *colormapP =
         pnm_computetuplefreqtable(mappamP, maptuples, MAXCOLORS, &colors);
     if (*colormapP == NULL)
         pm_error("too many colors in colormap!");
@@ -334,7 +366,7 @@ computeColorMapFromMap(struct pam *   const mappamP,
 
 #define FS_SCALE 1024
 
-struct fserr {
+struct Fserr {
     unsigned int width;
         /* Width of the image being dithered */
     long ** thiserr;
@@ -359,20 +391,26 @@ struct fserr {
 
 
 static void
-randomizeError(long **      const err,
-               unsigned int const width,
-               unsigned int const depth) {
+randomizeError(long **       const err,
+               unsigned int  const width,
+               unsigned int  const depth,
+               struct Random const random) {
 /*----------------------------------------------------------------------------
    Set a random error in the range [-1 .. 1] (normalized via FS_SCALE)
    in the error array err[][].
 -----------------------------------------------------------------------------*/
+    unsigned int const seed = (random.init == RANDOM_WITHSEED) ?
+        random.seed : pm_randseed();
+
     unsigned int col;
 
-    srand(pm_randseed());
+    assert(random.init != RANDOM_NONE);
+
+    srand(seed);
 
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             err[plane][col] = rand() % (FS_SCALE * 2) - FS_SCALE;
     }
 }
@@ -390,7 +428,7 @@ zeroError(long **      const err,
 
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             err[plane][col] = 0;
     }
 }
@@ -398,7 +436,7 @@ zeroError(long **      const err,
 
 
 static void
-fserrSetForward(struct fserr * const fserrP) {
+fserr_setForward(struct Fserr * const fserrP) {
 
     fserrP->fsForward = TRUE;
     fserrP->begCol = 0;
@@ -409,7 +447,7 @@ fserrSetForward(struct fserr * const fserrP) {
 
 
 static void
-fserrSetBackward(struct fserr * const fserrP) {
+fserr_setBackward(struct Fserr * const fserrP) {
 
     fserrP->fsForward = FALSE;
     fserrP->begCol = fserrP->width - 1;
@@ -420,9 +458,9 @@ fserrSetBackward(struct fserr * const fserrP) {
 
 
 static void
-initFserr(struct pam *   const pamP,
-          struct fserr * const fserrP,
-          bool           const initRandom) {
+fserr_init(struct pam *   const pamP,
+           struct Fserr * const fserrP,
+           struct Random  const random) {
 /*----------------------------------------------------------------------------
    Initialize the Floyd-Steinberg error vectors
 -----------------------------------------------------------------------------*/
@@ -440,7 +478,7 @@ initFserr(struct pam *   const pamP,
     if (fserrP->nexterr == NULL)
         pm_error("Out of memory allocating Floyd-Steinberg structures "
                  "for depth %u", pamP->depth);
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         MALLOCARRAY(fserrP->thiserr[plane], fserrSize);
         if (fserrP->thiserr[plane] == NULL)
@@ -452,24 +490,24 @@ initFserr(struct pam *   const pamP,
                      "for Plane %u, size %u", plane, fserrSize);
     }
 
-    if (initRandom)
-        randomizeError(fserrP->thiserr, fserrSize, pamP->depth);
+    if (random.init != RANDOM_NONE)
+        randomizeError(fserrP->thiserr, fserrSize, pamP->depth, random);
     else
         zeroError(fserrP->thiserr, fserrSize, pamP->depth);
 
-    fserrSetForward(fserrP);
+    fserr_setForward(fserrP);
 }
 
 
 
 static void
-floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
+floydInitRow(struct pam * const pamP, struct Fserr * const fserrP) {
 
     int col;
-    
+
     for (col = 0; col < pamP->width + 2; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane) 
+        for (plane = 0; plane < pamP->depth; ++plane)
             fserrP->nexterr[plane][col] = 0;
     }
 }
@@ -477,10 +515,10 @@ floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
 
 
 static void
-floydAdjustColor(struct pam *   const pamP, 
-                 tuple          const intuple, 
-                 tuple          const outtuple, 
-                 struct fserr * const fserrP, 
+floydAdjustColor(struct pam *   const pamP,
+                 tuple          const intuple,
+                 tuple          const outtuple,
+                 struct Fserr * const fserrP,
                  int            const col) {
 /*----------------------------------------------------------------------------
   Use Floyd-Steinberg errors to adjust actual color.
@@ -497,10 +535,10 @@ floydAdjustColor(struct pam *   const pamP,
 
 
 static void
-floydPropagateErr(struct pam *   const pamP, 
-                  struct fserr * const fserrP, 
-                  int            const col, 
-                  tuple          const oldtuple, 
+floydPropagateErr(struct pam *   const pamP,
+                  struct Fserr * const fserrP,
+                  int            const col,
+                  tuple          const oldtuple,
                   tuple          const newtuple) {
 /*----------------------------------------------------------------------------
   Propagate Floyd-Steinberg error terms.
@@ -515,7 +553,7 @@ floydPropagateErr(struct pam *   const pamP,
         long const newSample = newtuple[plane];
         long const oldSample = oldtuple[plane];
         long const err = (oldSample - newSample) * FS_SCALE;
-            
+
         if (fserrP->fsForward) {
             fserrP->thiserr[plane][col + 2] += ( err * 7 ) / 16;
             fserrP->nexterr[plane][col    ] += ( err * 3 ) / 16;
@@ -533,7 +571,7 @@ floydPropagateErr(struct pam *   const pamP,
 
 
 static void
-floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
+floydSwitchDir(struct pam * const pamP, struct Fserr * const fserrP) {
 
     unsigned int plane;
 
@@ -544,9 +582,9 @@ floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
     }
 
     if (fserrP->fsForward)
-        fserrSetBackward(fserrP);
+        fserr_setBackward(fserrP);
     else
-        fserrSetForward(fserrP);
+        fserr_setForward(fserrP);
 }
 
 
@@ -567,7 +605,7 @@ struct colormapFinder {
         /* The value by which our intermediate distance calculations
            have to be divided to make sure we don't overflow our
            unsigned int data structure.
-           
+
            To the extent 'distanceDivider' is greater than 1, closest
            color results will be approximate -- there could
            conceivably be a closer one that we miss.
@@ -590,13 +628,13 @@ createColormapFinder(struct pam *             const pamP,
     colormapFinderP->colors = colors;
 
     {
-        unsigned int const maxHandleableSqrDiff = 
+        unsigned int const maxHandleableSqrDiff =
             (unsigned int)UINT_MAX / pamP->depth;
-        
+
         if (SQR(pamP->maxval) > maxHandleableSqrDiff)
             colormapFinderP->distanceDivider = (unsigned int)
                 (SQR(pamP->maxval) / maxHandleableSqrDiff + 0.1 + 1.0);
-                /* The 0.1 is a fudge factor to keep us out of rounding 
+                /* The 0.1 is a fudge factor to keep us out of rounding
                    trouble.  The 1.0 effects a round-up.
                 */
         else
@@ -664,8 +702,8 @@ searchColormapClose(struct pam *            const pamP,
         newdist = 0;
 
         for (plane=0; plane < pamP->depth; ++plane) {
-            newdist += 
-                SQR(tuple[plane] - colorFinderP->colormap[i]->tuple[plane]) 
+            newdist +=
+                SQR(tuple[plane] - colorFinderP->colormap[i]->tuple[plane])
                 / colorFinderP->distanceDivider;
         }
         if (newdist < dist) {
@@ -695,15 +733,15 @@ searchColormapExact(struct pam *            const pamP,
 -----------------------------------------------------------------------------*/
     unsigned int i;
     bool found;
-    
+
     found = FALSE;  /* initial value */
     for (i = 0; i < colorFinderP->colors && !found; ++i) {
         unsigned int plane;
         found = TRUE;  /* initial assumption */
-        for (plane=0; plane < pamP->depth; ++plane) 
-            if (tuple[plane] != colorFinderP->colormap[i]->tuple[plane]) 
+        for (plane=0; plane < pamP->depth; ++plane)
+            if (tuple[plane] != colorFinderP->colormap[i]->tuple[plane])
                 found = FALSE;
-        if (found) 
+        if (found)
             *colormapIndexP = i;
     }
     *foundP = found;
@@ -712,11 +750,11 @@ searchColormapExact(struct pam *            const pamP,
 
 
 static void
-lookupThroughHash(struct pam *            const pamP, 
-                  tuple                   const tuple, 
+lookupThroughHash(struct pam *            const pamP,
+                  tuple                   const tuple,
                   bool                    const needExactMatch,
                   struct colormapFinder * const colorFinderP,
-                  tuplehash               const colorhash,       
+                  tuplehash               const colorhash,
                   int *                   const colormapIndexP,
                   bool *                  const usehashP) {
 /*----------------------------------------------------------------------------
@@ -748,11 +786,11 @@ lookupThroughHash(struct pam *            const pamP,
                                 colormapIndexP, &found);
             if (!found)
                 *colormapIndexP = -1;
-        } else 
+        } else
             searchColormapClose(pamP, tuple, colorFinderP, colormapIndexP);
         if (*usehashP) {
             int fits;
-            pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP, 
+            pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP,
                                &fits);
             if (!fits) {
                 pm_message("out of memory adding to hash table; "
@@ -771,7 +809,7 @@ mapTuple(struct pam *            const pamP,
          tuple                   const defaultColor,
          tupletable              const colormap,
          struct colormapFinder * const colorFinderP,
-         tuplehash               const colorhash, 
+         tuplehash               const colorhash,
          bool *                  const usehashP,
          tuple                   const outTuple,
          bool *                  const missingP) {
@@ -781,7 +819,7 @@ mapTuple(struct pam *            const pamP,
            there is no usable color in the color map.
         */
 
-    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP, 
+    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP,
                       colorhash, &colormapIndex, usehashP);
 
     if (colormapIndex == -1) {
@@ -800,12 +838,12 @@ mapTuple(struct pam *            const pamP,
 
 static void
 convertRowStraight(struct pam *            const inpamP,
-                   struct pam *            const outpamP, 
+                   struct pam *            const outpamP,
                    tuple                         inrow[],
                    depthAdjustment         const depthAdjustment,
                    tupletable              const colormap,
                    struct colormapFinder * const colorFinderP,
-                   tuplehash               const colorhash, 
+                   tuplehash               const colorhash,
                    bool *                  const usehashP,
                    tuple                   const defaultColor,
                    tuple                         outrow[],
@@ -822,7 +860,7 @@ convertRowStraight(struct pam *            const inpamP,
 -----------------------------------------------------------------------------*/
     unsigned int col;
     unsigned int missingCount;
-    
+
     /* The following modify tuplerow, to make it consistent with
      *outpamP instead of *inpamP.
      */
@@ -833,7 +871,7 @@ convertRowStraight(struct pam *            const inpamP,
     adjustDepthRow(outrow, outpamP->width, depthAdjustment);
 
     missingCount = 0;  /* initial value */
-    
+
     for (col = 0; col < outpamP->width; ++col) {
         bool missing;
         mapTuple(outpamP, outrow[col], defaultColor,
@@ -856,10 +894,10 @@ convertRowDither(struct pam *            const inpamP,
                  depthAdjustment         const depthAdjustment,
                  tupletable              const colormap,
                  struct colormapFinder * const colorFinderP,
-                 tuplehash               const colorhash, 
+                 tuplehash               const colorhash,
                  bool *                  const usehashP,
                  tuple                   const defaultColor,
-                 struct fserr *          const fserrP,
+                 struct Fserr *          const fserrP,
                  tuple                         outrow[],
                  unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
@@ -885,7 +923,7 @@ convertRowDither(struct pam *            const inpamP,
     floydInitRow(inpamP, fserrP);
 
     missingCount = 0;  /* initial value */
-    
+
     for (col = fserrP->begCol; col != fserrP->endCol; col += fserrP->step) {
         bool missing;
 
@@ -929,11 +967,11 @@ convertRow(struct pam *            const inpamP,
            depthAdjustment               depthAdjustment,
            tupletable              const colormap,
            struct colormapFinder * const colorFinderP,
-           tuplehash               const colorhash, 
+           tuplehash               const colorhash,
            bool *                  const usehashP,
-           bool                    const floyd, 
+           bool                    const floyd,
            tuple                   const defaultColor,
-           struct fserr *          const fserrP,
+           struct Fserr *          const fserrP,
            tuple                         outrow[],
            unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
@@ -959,7 +997,7 @@ convertRow(struct pam *            const inpamP,
                          depthAdjustment, colormap, colorFinderP, colorhash,
                          usehashP, defaultColor,
                          fserrP, outrow, missingCountP);
-    else 
+    else
         convertRowStraight(inpamP, outpamP, inrow,
                            depthAdjustment, colormap, colorFinderP, colorhash,
                            usehashP, defaultColor,
@@ -969,14 +1007,14 @@ convertRow(struct pam *            const inpamP,
 
 
 static void
-copyRaster(struct pam *       const inpamP, 
-           struct pam *       const outpamP,
-           tupletable         const colormap, 
-           unsigned int       const colormapSize,
-           bool               const floyd, 
-           bool               const randomize,
-           tuple              const defaultColor, 
-           unsigned int *     const missingCountP) {
+copyRaster(struct pam *   const inpamP,
+           struct pam *   const outpamP,
+           tupletable     const colormap,
+           unsigned int   const colormapSize,
+           bool           const floyd,
+           struct Random  const random,
+           tuple          const defaultColor,
+           unsigned int * const missingCountP) {
 
     tuplehash const colorhash = pnm_createtuplehash();
 
@@ -992,7 +1030,7 @@ copyRaster(struct pam *       const inpamP,
     depthAdjustment depthAdjustment;
     struct colormapFinder * colorFinderP;
     bool usehash;
-    struct fserr fserr;
+    struct Fserr fserr;
     int row;
 
     workpam = *outpamP;
@@ -1017,7 +1055,7 @@ copyRaster(struct pam *       const inpamP,
     createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP);
 
     if (floyd)
-        initFserr(inpamP, &fserr, randomize);
+        fserr_init(inpamP, &fserr, random);
 
     *missingCountP = 0;  /* initial value */
 
@@ -1030,9 +1068,9 @@ copyRaster(struct pam *       const inpamP,
                    depthAdjustment, colormap, colorFinderP, colorhash,
                    &usehash, floyd, defaultColor,
                    &fserr,  outrow, &missingCount);
-        
+
         *missingCountP += missingCount;
-        
+
         pnm_writepamrow(outpamP, outrow);
     }
     destroyColormapFinder(colorFinderP);
@@ -1046,10 +1084,10 @@ copyRaster(struct pam *       const inpamP,
 static void
 remap(FILE *             const ifP,
       const struct pam * const outpamCommonP,
-      tupletable         const colormap, 
+      tupletable         const colormap,
       unsigned int       const colormapSize,
       bool               const floyd,
-      bool               const randomize,
+      struct Random      const random,
       tuple              const defaultColor,
       bool               const verbose) {
 /*----------------------------------------------------------------------------
@@ -1075,7 +1113,7 @@ remap(FILE *             const ifP,
             */
 
         pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
-    
+
         outpam = *outpamCommonP;
         outpam.width  = inpam.width;
         outpam.height = inpam.height;
@@ -1086,13 +1124,13 @@ remap(FILE *             const ifP,
            convert the input to the output depth.
         */
         pnm_setminallocationdepth(&inpam, outpam.depth);
-    
+
         copyRaster(&inpam, &outpam, colormap, colormapSize, floyd,
-                   randomize, defaultColor, &missingCount);
-        
+                   random, defaultColor, &missingCount);
+
         if (verbose)
             pm_message("%u pixels not matched in color map", missingCount);
-        
+
         pnm_nextimage(ifP, &eof);
     }
 }
@@ -1130,7 +1168,7 @@ processMapFile(const char *   const mapFileName,
 
     pnm_freepamarray(maptuples, &mappam);
 
-    *outpamCommonP = mappam; 
+    *outpamCommonP = mappam;
     outpamCommonP->file = stdout;
 }
 
@@ -1142,7 +1180,7 @@ getSpecifiedMissingColor(struct pam * const pamP,
                          tuple *      const specColorP) {
 
     tuple specColor;
-                             
+
     specColor = pnm_allocpamtuple(pamP);
 
     if (colorName) {
@@ -1213,8 +1251,8 @@ main(int argc, const char * argv[] ) {
         break;
     }
 
-    remap(ifP, &outpamCommon, colormap, colormapSize, 
-          cmdline.floyd, !cmdline.norandom, defaultColor,
+    remap(ifP, &outpamCommon, colormap, colormapSize,
+          cmdline.floyd, cmdline.random, defaultColor,
           cmdline.verbose);
 
     pnm_freepamtuple(firstColor);