about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/Makefile4
-rw-r--r--editor/pamfunc.c28
-rw-r--r--editor/pnmcrop.c163
-rw-r--r--editor/pnmnorm.c152
-rwxr-xr-xeditor/ppmbrighten60
-rw-r--r--editor/ppmbrighten.c218
6 files changed, 305 insertions, 320 deletions
diff --git a/editor/Makefile b/editor/Makefile
index 5b12e4ca..88409dad 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -32,7 +32,7 @@ PORTBINARIES = pamaddnoise pamaltsat pambackground pambrighten pamcomp pamcut \
 	       pnmnlfilt pnmnorm pnmpad pnmpaste \
 	       pnmremap pnmrotate \
 	       pnmscalefixed pnmshear pnmsmooth pnmstitch pnmtile \
-	       ppmbrighten ppmchange ppmcolormask \
+	       ppmchange ppmcolormask \
 	       ppmdim ppmdist ppmdither ppmdraw \
 	       ppmflash ppmlabel ppmmix \
 
@@ -44,7 +44,7 @@ NOMERGEBINARIES =
 MERGEBINARIES = $(PORTBINARIES)
 
 BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
-SCRIPTS = pnmflip ppmfade ppmquant ppmshadow \
+SCRIPTS = ppmbrighten pnmflip ppmfade ppmquant ppmshadow \
 	  pamstretch-gen pnmmargin pnmquant pnmquantall 
 
 OBJECTS = $(BINARIES:%=%.o)
diff --git a/editor/pamfunc.c b/editor/pamfunc.c
index 454e6d63..b85cfe9b 100644
--- a/editor/pamfunc.c
+++ b/editor/pamfunc.c
@@ -10,7 +10,7 @@
   ENHANCEMENT IDEAS:
 
   1) speed up by doing integer arithmetic instead of floating point for
-  multiply/divide where possible.  Especially when multiplying by an 
+  multiply/divide where possible.  Especially when multiplying by an
   integer.
 
   2) speed up by not transforming the raster in the idempotent cases
@@ -20,6 +20,7 @@
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
 
@@ -78,7 +79,7 @@ parseHex(const char * const hexString) {
     return retval;
 }
 
-         
+
 
 static void
 parseCommandLine(int argc, const char ** const argv,
@@ -103,7 +104,7 @@ parseCommandLine(int argc, const char ** const argv,
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "multiplier", OPT_FLOAT,  &cmdlineP->u.multiplier, 
+    OPTENT3(0,   "multiplier", OPT_FLOAT,  &cmdlineP->u.multiplier,
             &multiplierSpec, 0);
     OPTENT3(0,   "divisor",    OPT_FLOAT,  &cmdlineP->u.divisor,
             &divisorSpec,    0);
@@ -150,12 +151,12 @@ parseCommandLine(int argc, const char ** const argv,
     if (multiplierSpec) {
         cmdlineP->function = FN_MULTIPLY;
         if (cmdlineP->u.multiplier < 0)
-            pm_error("Multiplier must be nonnegative.  You specified %f", 
+            pm_error("Multiplier must be nonnegative.  You specified %f",
                      cmdlineP->u.multiplier);
     } else if (divisorSpec) {
         cmdlineP->function = FN_DIVIDE;
         if (cmdlineP->u.divisor < 0)
-            pm_error("Divisor must be nonnegative.  You specified %f", 
+            pm_error("Divisor must be nonnegative.  You specified %f",
                      cmdlineP->u.divisor);
     } else if (adderSpec) {
         cmdlineP->function = FN_ADD;
@@ -180,20 +181,20 @@ parseCommandLine(int argc, const char ** const argv,
         cmdlineP->function = FN_SHIFTLEFT;
     } else if (shiftrightSpec) {
         cmdlineP->function = FN_SHIFTRIGHT;
-    } else 
+    } else
         pm_error("You must specify one of -multiplier, -divisor, "
                  "-adder, -subtractor, -min, -max, "
                  "-and, -or, -xor, -not, -shiftleft, or -shiftright");
-        
+
     if (argc-1 > 1)
         pm_error("Too many arguments (%d).  File spec is the only argument.",
                  argc-1);
 
     if (argc-1 < 1)
         cmdlineP->inputFileName = "-";
-    else 
+    else
         cmdlineP->inputFileName = argv[1];
-    
+
     free(option_def);
 }
 
@@ -336,7 +337,7 @@ applyFunction(struct CmdlineInfo const cmdline,
            1/cmdline.u.divisor instead of divide by cmdline.u.divisor,
            so we compute that here.  Note that if the function isn't
            divide, both cmdline.u.divisor and oneOverDivisor are
-           meaningless.  
+           meaningless.
         */
     unsigned int col;
 
@@ -388,7 +389,7 @@ applyFunction(struct CmdlineInfo const cmdline,
             outputRow[col][plane] = MIN(outpam.maxval, outSample);
         }
     }
-}                
+}
 
 
 
@@ -422,6 +423,9 @@ main(int argc, const char *argv[]) {
     planTransform(cmdline, inpam.maxval, outpam.format,
                   &outpam.maxval, &mustChangeRaster);
 
+    if (outpam.maxval > 1 && strneq(outpam.tuple_type, "BLACKANDWHITE", 13))
+        strcpy(outpam.tuple_type, "");
+
     pnm_writepaminit(&outpam);
 
     outputRow = pnm_allocpamrow(&outpam);
@@ -440,7 +444,7 @@ main(int argc, const char *argv[]) {
     pnm_freepamrow(inputRow);
     pm_close(inpam.file);
     pm_close(outpam.file);
-    
+
     return 0;
 }
 
diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c
index f07340eb..43027da0 100644
--- a/editor/pnmcrop.c
+++ b/editor/pnmcrop.c
@@ -1123,63 +1123,135 @@ noCrops(struct CmdlineInfo const cmdline) {
 
 
 
+static void
+divideAllBackgroundIntoBorders(unsigned int   const totalSz,
+                               bool           const wantCropSideA,
+                               bool           const wantCropSideB,
+                               unsigned int   const wantMargin,
+                               unsigned int * const sideASzP,
+                               unsigned int * const sideBSzP) {
+/*----------------------------------------------------------------------------
+   Divide up an all-background space into fictional borders (that can be
+   trimmed or padded).
+
+   If there is to be a margin, those borders touch - the entire image is
+   borders.  But if there is not to be a margin, there has to be one pixel,
+   row, or column between the borders so that there is something left after we
+   crop off those borders (because there's no such thing as a zero-pixel
+   image).
+
+   This function does the borders in one direction - top and bottom or left
+   and right.  Top or left is called "Side A"; bottom or right is called "Side
+   B".  'totalSz' is the width of the image in the top/bottom case and the
+   height of the image in the left/right case.
+-----------------------------------------------------------------------------*/
+    unsigned int sideASz, sideBSz;
+
+    if (wantCropSideA && wantCropSideB) {
+        sideASz = totalSz/2;
+        if (wantMargin)
+            sideBSz = totalSz - sideASz;
+        else
+            sideBSz = totalSz - sideASz - 1;
+    } else if (wantCropSideA) {
+        if (wantMargin)
+            sideASz = totalSz;
+        else
+            sideASz = totalSz - 1;
+        sideBSz = 0;
+    } else if (wantCropSideB) {
+        sideASz = 0;
+        if (wantMargin)
+            sideBSz = totalSz;
+        else
+            sideBSz = totalSz - 1;
+    } else {
+        sideASz = 0;
+        sideBSz = 0;
+    }
+    *sideASzP = sideASz;
+    *sideBSzP = sideBSz;
+}
+
+
+
+static CropOp
+oneSideCrop(bool         const wantCrop,
+            unsigned int const borderSz,
+            unsigned int const margin) {
+
+    CropOp retval;
+
+    if (wantCrop) {
+        if (borderSz >= margin) {
+            retval.removeSize = borderSz - margin;
+            retval.padSize    = 0;
+        } else {
+            retval.removeSize = 0;
+            retval.padSize    = margin - borderSz;
+        }
+    } else {
+        retval.removeSize = 0;
+        retval.padSize    = 0;
+    }
+
+    return retval;
+}
+
+
+
 static CropSet
 extremeCrops(struct CmdlineInfo const cmdline,
              unsigned int       const cols,
              unsigned int       const rows) {
 /*----------------------------------------------------------------------------
-   Crops that crop as much as possible, reducing output to a single pixel.
+   Crops that crop as much as possible, reducing output to a single
+   row, column, or pixel, plus margins.
 -----------------------------------------------------------------------------*/
     CropSet retval;
 
+    unsigned int leftBorderSz, rghtBorderSz;
+    unsigned int topBorderSz, botBorderSz;
+
     if (cmdline.verbose)
         pm_message("Input image has no distinction between "
                    "border and content");
 
-    /* We can't just pick a representative pixel, say top-left corner.
-       If -top and/or -bottom was specified but not -left and -right,
-       the output should be one row, not a single pixel.
+    /* Note that the "entirely background" image may have several colors: this
+       happens when -closeness was specified.  That means we can't just build
+       up an image from background color - we actually have to preserve some
+       of the original image.
 
-       The "entirely background" image may have several colors: this
-       happens when -closeness was specified.
+       We divide the background into individual borders, with a foreground of
+       either nothing at all or a single row, column, or pixel, then crop
+       those fictional borders the same as if they were real.  The "nothing
+       at all" case is feasible only when there is to be a margin, because
+       otherwise cropping the borders would leave nothing.
     */
 
-    if (cmdline.wantCrop[LEFT] && cmdline.wantCrop[RIGHT]) {
-        retval.op[LEFT ].removeSize = cols / 2;
-        retval.op[RIGHT].removeSize = cols - retval.op[LEFT].removeSize -1;
-    } else if (cmdline.wantCrop[LEFT]) {
-        retval.op[LEFT ].removeSize = cols - 1;
-        retval.op[RIGHT].removeSize = 0;
-    } else if (cmdline.wantCrop[RIGHT]) {
-        retval.op[LEFT ].removeSize = 0;
-        retval.op[RIGHT].removeSize = cols - 1;
-    } else {
-        retval.op[LEFT ].removeSize = 0;
-        retval.op[RIGHT].removeSize = 0;
-    }
-
-    if (cmdline.wantCrop[TOP] && cmdline.wantCrop[BOTTOM]) {
-        retval.op[ TOP  ].removeSize = rows / 2;
-        retval.op[BOTTOM].removeSize = rows - retval.op[TOP].removeSize -1;
-    } else if (cmdline.wantCrop[TOP]) {
-        retval.op[ TOP  ].removeSize = rows - 1;
-        retval.op[BOTTOM].removeSize = 0;
-    } else if (cmdline.wantCrop[BOTTOM]) {
-        retval.op[ TOP  ].removeSize = 0;
-        retval.op[BOTTOM].removeSize = rows - 1;
-    } else {
-        retval.op[ TOP  ].removeSize = 0;
-        retval.op[BOTTOM].removeSize = 0;
-    }
+    divideAllBackgroundIntoBorders(cols,
+                                   cmdline.wantCrop[LEFT],
+                                   cmdline.wantCrop[RIGHT],
+                                   cmdline.margin > 0,
+                                   &leftBorderSz,
+                                   &rghtBorderSz);
+
+    divideAllBackgroundIntoBorders(rows,
+                                   cmdline.wantCrop[TOP],
+                                   cmdline.wantCrop[BOTTOM],
+                                   cmdline.margin > 0,
+                                   &topBorderSz,
+                                   &botBorderSz);
+
+    retval.op[LEFT  ] =
+        oneSideCrop(cmdline.wantCrop[LEFT  ], leftBorderSz, cmdline.margin);
+    retval.op[RIGHT ] =
+        oneSideCrop(cmdline.wantCrop[RIGHT ], rghtBorderSz, cmdline.margin);
+    retval.op[TOP   ] =
+        oneSideCrop(cmdline.wantCrop[TOP   ], topBorderSz,  cmdline.margin);
+    retval.op[BOTTOM] =
+        oneSideCrop(cmdline.wantCrop[BOTTOM], botBorderSz,  cmdline.margin);
 
-    if (cmdline.margin > 0)
-        pm_message ("-margin value %u ignored", cmdline.margin);
-
-    {
-        EdgeLocation i;
-        for (i = 0; i < ARRAY_SIZE(retval.op); ++i)
-            retval.op[i].padSize = 0;
-    }
     return retval;
 }
 
@@ -1329,12 +1401,9 @@ cropOneImage(struct CmdlineInfo const cmdline,
             pm_error("The image is entirely background; "
                      "there is nothing to crop.");
             break;
-        case BLANK_PASS:
-            crop = noCrops(cmdline);                   break;
-        case BLANK_MINIMIZE:
-            crop = extremeCrops(cmdline, cols, rows);  break;
-        case BLANK_MAXCROP:
-            crop = maxcropReport(cmdline, cols, rows); break;
+        case BLANK_PASS:     crop = noCrops(cmdline);                   break;
+        case BLANK_MINIMIZE: crop = extremeCrops(cmdline, cols, rows);  break;
+        case BLANK_MAXCROP:  crop = maxcropReport(cmdline, cols, rows); break;
         }
     } else {
         crop = crops(cmdline, oldBorder);
diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c
index 20d7eb04..8c25df80 100644
--- a/editor/pnmnorm.c
+++ b/editor/pnmnorm.c
@@ -38,7 +38,7 @@
 
 enum brightMethod {BRIGHT_LUMINOSITY, BRIGHT_COLORVALUE, BRIGHT_SATURATION};
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -70,7 +70,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 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.
@@ -265,7 +265,12 @@ buildHistogram(FILE *   const ifp,
 static xelval
 minimumValue(const unsigned int * const hist,
              unsigned int         const highest) {
+/*----------------------------------------------------------------------------
+   The minimum brightness in the image, according to histogram 'hist',
+   which goes up to 'highest'.
 
+   Abort the program if there are no pixels of any brightness.
+-----------------------------------------------------------------------------*/
     xelval i;
     bool foundOne;
 
@@ -288,7 +293,12 @@ minimumValue(const unsigned int * const hist,
 static xelval
 maximumValue(const unsigned int * const hist,
              unsigned int         const highest) {
+/*----------------------------------------------------------------------------
+   The maximum brightness in the image, according to histogram 'hist',
+   which goes up to 'highest'.
 
+   Abort the program if there are no pixels of any brightness.
+-----------------------------------------------------------------------------*/
     xelval i;
     bool foundOne;
 
@@ -308,12 +318,32 @@ maximumValue(const unsigned int * const hist,
 
 
 
+static unsigned int
+brightnessCount(const unsigned int * const hist,
+                unsigned int         const highest) {
+/*----------------------------------------------------------------------------
+   The number of distinct brightnesses in the image according to
+   histogram 'hist', which goes up to brightness 'highest'.
+-----------------------------------------------------------------------------*/
+    xelval i;
+    unsigned int nonzeroCount;
+
+    for (i = 0, nonzeroCount = 0; i <= highest; ++i) {
+        if (hist[i] > 0)
+            ++nonzeroCount;
+    }
+
+    return nonzeroCount;
+}
+
+
+
 static void
-computeBottomPercentile(unsigned int         hist[],
-                        unsigned int   const highest,
-                        unsigned int   const total,
-                        float          const percent,
-                        unsigned int * const percentileP) {
+computeBottomPercentile(const unsigned int * const hist,
+                        unsigned int         const highest,
+                        unsigned int         const total,
+                        float                const percent,
+                        unsigned int *       const percentileP) {
 /*----------------------------------------------------------------------------
    Compute the lowest index of hist[] such that the sum of the hist[]
    values with that index and lower represent at least 'percent' per cent of
@@ -341,11 +371,11 @@ computeBottomPercentile(unsigned int         hist[],
 
 
 static void
-computeTopPercentile(unsigned int         hist[],
-                     unsigned int   const highest,
-                     unsigned int   const total,
-                     float          const percent,
-                     unsigned int * const percentileP) {
+computeTopPercentile(const unsigned int * const hist,
+                     unsigned int         const highest,
+                     unsigned int         const total,
+                     float                const percent,
+                     unsigned int *       const percentileP) {
 /*----------------------------------------------------------------------------
    Compute the highest index of hist[] such that the sum of the hist[]
    values with that index and higher represent 'percent' per cent of
@@ -496,13 +526,68 @@ disOverlap(xelval   const reqBvalue,
 
 
 
+static xelval
+resolvedPctBvalue(const unsigned int * const hist,
+                  unsigned int         const totalPixelCt,
+                  xelval               const maxval,
+                  struct CmdlineInfo   const cmdline) {
+
+    xelval retval;
+
+    if (cmdline.bsingle)
+        retval = minimumValue(hist, maxval);
+    else if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
+        retval = cmdline.bvalue;
+    } else {
+        xelval percentBvalue;
+
+        computeBottomPercentile(hist, maxval, totalPixelCt, cmdline.bpercent,
+                                &percentBvalue);
+        if (cmdline.bvalueSpec)
+            retval = MIN(percentBvalue, cmdline.bvalue);
+        else
+            retval = percentBvalue;
+    }
+    return retval;
+}
+
+
+
+static xelval
+resolvedPctWvalue(const unsigned int * const hist,
+                  unsigned int         const totalPixelCt,
+                  xelval               const maxval,
+                  struct CmdlineInfo   const cmdline) {
+
+    xelval retval;
+
+    if (cmdline.wsingle)
+        retval = maximumValue(hist, maxval);
+    else if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
+        retval = cmdline.wvalue;
+    } else {
+        xelval percentWvalue;
+
+        computeTopPercentile(hist, maxval, totalPixelCt, cmdline.wpercent,
+                             &percentWvalue);
+        if (cmdline.wvalueSpec)
+            retval = MAX(percentWvalue, cmdline.wvalue);
+        else
+            retval = percentWvalue;
+    }
+
+    return retval;
+}
+
+
+
 static void
 resolvePercentParams(FILE *             const ifP,
                      unsigned int       const cols,
                      unsigned int       const rows,
                      xelval             const maxval,
                      int                const format,
-                     struct cmdlineInfo const cmdline,
+                     struct CmdlineInfo const cmdline,
                      xelval *           const bvalueP,
                      xelval *           const wvalueP) {
 /*----------------------------------------------------------------------------
@@ -523,32 +608,17 @@ resolvePercentParams(FILE *             const ifP,
         buildHistogram(ifP, cols, rows, maxval, format, hist,
                        cmdline.brightMethod);
 
-        if (cmdline.bsingle)
-            *bvalueP = minimumValue(hist, maxval);
-        else if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
-            *bvalueP = cmdline.bvalue;
+        if (!cmdline.bvalueSpec && !cmdline.wvalueSpec &&
+            brightnessCount(hist, maxval) < 2) {
+            /* Special case - you can't stretch a single brightness to both
+               ends.  So just don't stretch at all.
+            */
+            *bvalueP = 0;
+            *wvalueP = maxval;
         } else {
-            xelval percentBvalue;
-            computeBottomPercentile(hist, maxval, cols*rows, cmdline.bpercent,
-                                    &percentBvalue);
-            if (cmdline.bvalueSpec)
-                *bvalueP = MIN(percentBvalue, cmdline.bvalue);
-            else
-                *bvalueP = percentBvalue;
-        }
+            *bvalueP = resolvedPctBvalue(hist, cols * rows, maxval, cmdline);
 
-        if (cmdline.wsingle)
-            *wvalueP = maximumValue(hist, maxval);
-        else if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
-            *wvalueP = cmdline.wvalue;
-        } else {
-            xelval percentWvalue;
-            computeTopPercentile(hist, maxval, cols*rows, cmdline.wpercent,
-                                 &percentWvalue);
-            if (cmdline.wvalueSpec)
-                *wvalueP = MAX(percentWvalue, cmdline.wvalue);
-            else
-                *wvalueP = percentWvalue;
+            *wvalueP = resolvedPctWvalue(hist, cols * rows, maxval, cmdline);
         }
         free(hist);
     }
@@ -562,7 +632,7 @@ computeEndValues(FILE *             const ifP,
                  int                const rows,
                  xelval             const maxval,
                  int                const format,
-                 struct cmdlineInfo const cmdline,
+                 struct CmdlineInfo const cmdline,
                  xelval *           const bvalueP,
                  xelval *           const wvalueP,
                  bool *             const quadraticP,
@@ -629,7 +699,7 @@ computeLinearTransfer(xelval   const bvalue,
     unsigned int val;
     /* The following for structure is a hand optimization of this one:
        for (i = bvalue; i <= wvalue; ++i)
-       newBrightness[i] = (i-bvalue)*maxval/range);
+           newBrightness[i] = (i-bvalue)*maxval/range);
        (with proper rounding)
     */
     for (i = bvalue, val = range/2;
@@ -937,8 +1007,8 @@ reportTransferParm(bool   const quadratic,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
-    FILE *ifP;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
     pm_filepos imagePos;
     xelval maxval;
     int rows, cols, format;
diff --git a/editor/ppmbrighten b/editor/ppmbrighten
new file mode 100755
index 00000000..f5de436f
--- /dev/null
+++ b/editor/ppmbrighten
@@ -0,0 +1,60 @@
+#! /bin/sh
+
+# This is just for backward compatibility.  New applications should use
+# 'pambrighten'.
+
+# We don't try very hard to respond well to invalid syntax, because backward
+# compatibility is mostly like existing, working applications.
+
+pambrightenOpts=''
+normalize='no'
+expectValue='no'
+
+for word in "$@"; do
+
+    if test "$expectValue" = 'yes'; then
+        # This is the value of an option, like "40" in "-saturation 40"
+        pambrightenOpts="$pambrightenOpts $word"
+        expectValue='no'
+    else
+        # 'word_one_hyphen' is 'word' except if 'word' is a double-hyphen
+        # option, 'word_one_hyphen' is the single-hyphen version of it.
+        # E.g. word=--saturation word_one_hyphen=-saturation .
+        word_one_hyphen=$(echo "$word" | sed s/^--/-/ )
+    
+        case $word_one_hyphen in
+            -version )
+                pambrighten -version; exit $?
+                ;;
+            -normalize|-normaliz|-normali|-normal|-norma|-norm|-nor|-no|-n)
+                normalize='yes'
+                ;;
+            -*=*)
+                pambrightenOpts="$pambrightenOpts $word"
+                # This is an option with value such as "-saturation=40"
+                ;;
+            -*)
+                pambrightenOpts="$pambrightenOpts $word"
+                # Starts with hyphen, no equals sign, so the next word is the
+                # option's value (note that the only valid ppmbrighten flag
+                # option is -normalized, handled above).
+                #
+                # E.g. "-saturation 40"
+                expectValue='yes'
+                ;;
+            *)
+                # Not an option or option value - only non-option argument
+                # ppmbrighten has is optional input file name
+                infile="$word"
+                ;;
+                
+        esac
+    fi
+done
+
+if test "$normalize" = 'yes'; then
+    pnmnorm -bsingle -wsingle -colorvalue -keephues $infile | \
+        pambrighten $pambrightenOpts | ppmtoppm
+else
+    pambrighten $pambrightenOpts $infile | ppmtoppm
+fi
diff --git a/editor/ppmbrighten.c b/editor/ppmbrighten.c
deleted file mode 100644
index 0446bb75..00000000
--- a/editor/ppmbrighten.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/*=============================================================================
-                              ppmbrighten
-===============================================================================
-  Change Value and Saturation of PPM image.
-=============================================================================*/
-
-#include "pm_c_util.h"
-#include "ppm.h"
-#include "shhopt.h"
-#include "mallocvar.h"
-
-struct CmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char * inputFileName;  /* '-' if stdin */
-    float saturation;
-    float value;
-    unsigned int normalize;
-};
-
-
-
-static void
-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.
-
-   If command line is internally inconsistent (invalid options, etc.),
-   issue error message to stderr and abort program.
-
-   Note that the strings we return are stored in the storage that
-   was passed to us as the argv array.  We also trash *argv.
------------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to pm_optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    unsigned int saturationSpec, valueSpec;
-    int saturationOpt, valueOpt;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "saturation",  OPT_INT,    &saturationOpt,
-            &saturationSpec,      0 );
-    OPTENT3(0, "value",       OPT_INT,    &valueOpt,
-            &valueSpec,           0 );
-    OPTENT3(0, "normalize",   OPT_FLAG,   NULL,
-            &cmdlineP->normalize, 0 );
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-    if (saturationSpec) {
-        if (saturationOpt < -100)
-            pm_error("Saturation reduction cannot be more than 100%%.  "
-                     "You specified %d", saturationOpt);
-        else
-            cmdlineP->saturation = 1.0 + (float)saturationOpt / 100;
-    } else
-        cmdlineP->saturation = 1.0;
-
-    if (valueSpec) {
-        if (valueOpt < -100)
-            pm_error("Value reduction cannot be more than 100%%.  "
-                     "You specified %d", valueOpt);
-        else
-            cmdlineP->value = 1.0 + (float)valueOpt / 100;
-    } else
-        cmdlineP->value = 1.0;
-
-    if (argc-1 < 1)
-        cmdlineP->inputFileName = "-";
-    else if (argc-1 == 1)
-        cmdlineP->inputFileName = argv[1];
-    else
-        pm_error("Program takes at most one argument:  file specification");
-}
-
-
-
-static void
-getMinMax(FILE *       const ifP,
-          unsigned int const cols,
-          unsigned int const rows,
-          pixval       const maxval,
-          int          const format,
-          double *     const minValueP,
-          double *     const maxValueP) {
-
-    pixel * pixelrow;
-    double minValue, maxValue;
-    unsigned int row;
-
-    pixelrow = ppm_allocrow(cols);
-
-    for (row = 0, minValue = 65536.0, maxValue = 0.0; row < rows; ++row) {
-        unsigned int col;
-
-        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
-
-        for (col = 0; col < cols; ++col) {
-            struct hsv const pixhsv =
-                ppm_hsv_from_color(pixelrow[col], maxval);
-
-            maxValue = MAX(maxValue, pixhsv.v);
-            minValue = MIN(minValue, pixhsv.v);
-        }
-    }
-    ppm_freerow(pixelrow);
-
-    *minValueP = minValue;
-    *maxValueP = maxValue;
-}
-
-
-
-int
-main(int argc, const char ** argv) {
-
-    double const EPSILON = 1.0e-5;
-    struct CmdlineInfo cmdline;
-    FILE * ifP;
-    pixel * pixelrow;
-    pixval maxval;
-    int rows, cols, format, row;
-    double minValue, maxValue;
-
-    pm_proginit(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    if (cmdline.normalize)
-        ifP = pm_openr_seekable(cmdline.inputFileName);
-    else
-        ifP = pm_openr(cmdline.inputFileName);
-
-    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
-
-    if (cmdline.normalize) {
-        pm_filepos rasterPos;
-        pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
-        getMinMax(ifP, cols, rows, maxval, format, &minValue, &maxValue);
-        pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
-        if (maxValue - minValue > EPSILON) {
-            pm_message("Minimum value %.0f%% of full intensity "
-                       "being remapped to zero.",
-                       (minValue * 100.0));
-            pm_message("Maximum value %.0f%% of full intensity "
-                       "being remapped to full.",
-                       (maxValue * 100.0));
-        } else
-            pm_message("Sole value of %.0f%% of full intensity "
-                       "not being remapped",
-                       (maxValue * 100.0));
-    }
-
-    pixelrow = ppm_allocrow(cols);
-
-    ppm_writeppminit(stdout, cols, rows, maxval, 0);
-
-    for (row = 0; row < rows; ++row) {
-        unsigned int col;
-
-        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
-
-        for (col = 0; col < cols; ++col) {
-            struct hsv pixhsv;
-
-            pixhsv = ppm_hsv_from_color(pixelrow[col], maxval);
-                /* initial value */
-
-            if (cmdline.normalize) {
-                if (maxValue - minValue > EPSILON)
-                    pixhsv.v = (pixhsv.v - minValue) / (maxValue - minValue);
-            }
-            pixhsv.s = pixhsv.s * cmdline.saturation;
-            pixhsv.s = MAX(0.0, MIN(1.0, pixhsv.s));
-            pixhsv.v = pixhsv.v * cmdline.value;
-            pixhsv.v = MAX(0.0, MIN(1.0, pixhsv.v));
-            pixelrow[col] = ppm_color_from_hsv(pixhsv, maxval);
-        }
-        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
-    }
-    ppm_freerow(pixelrow);
-
-    pm_close(ifP);
-
-    /* If the program failed, it previously aborted with nonzero exit status
-       via various function calls.
-    */
-    return 0;
-}
-
-
-
-/**
-** Copyright (C) 1989 by Jef Poskanzer.
-** Copyright (C) 1990 by Brian Moffet.
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-*/
-