about summary refs log tree commit diff
path: root/editor/pnmnorm.c
diff options
context:
space:
mode:
Diffstat (limited to 'editor/pnmnorm.c')
-rw-r--r--editor/pnmnorm.c185
1 files changed, 137 insertions, 48 deletions
diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c
index b36ad462..27d51115 100644
--- a/editor/pnmnorm.c
+++ b/editor/pnmnorm.c
@@ -29,9 +29,10 @@
 
 #include <assert.h>
 
-#include "pnm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pnm.h"
 
 enum brightMethod {BRIGHT_LUMINOSITY, BRIGHT_COLORVALUE, BRIGHT_SATURATION};
 
@@ -60,8 +61,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+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.  
@@ -116,7 +117,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!cmdlineP->wpercentSpec)
@@ -354,21 +355,85 @@ computeAdjustmentForExpansionLimit(xelval   const maxval,
 
 
 static void
-computeEndValues(FILE *             const ifp,
-                 int                const cols,
-                 int                const rows,
-                 xelval             const maxval,
-                 int                const format,
-                 struct cmdlineInfo const cmdline,
-                 xelval *           const bvalueP,
-                 xelval *           const wvalueP) {
+disOverlap(xelval   const reqBvalue,
+           xelval   const reqWvalue,
+           bool     const bIsFixed,
+           bool     const wIsFixed,
+           xelval   const maxval,
+           xelval * const nonOlapBvalueP,
+           xelval * const nonOlapWvalueP) {
 /*----------------------------------------------------------------------------
-   Figure out what original values will be translated to full white and
-   full black -- thus defining to what all the other values get translated.
+   Compute black and white end values that don't overlap, i.e. the
+   black value is darker than the white, from an initial attempt that
+   might overlap.
 
-   This may involve looking at the image.  The image is in the file
-   'ifp', which is positioned just past the header (at the raster).
-   Leave it positioned arbitrarily.
+   'req{B|W}value' is that initial attempt.  We return the
+   nonoverlapping version as *nonOlap{B|W}valueP.
+
+   '{b|w}IsFixed' means we cannot change that endpoint.
+
+   If both ends are fixed 'reqBvalue' and 'reqWvalue' overlap, we just
+   fail the program -- the user asked for the impossible.
+
+   Where one end is fixed and the other is not, we move the unfixed end
+   to be one unit above or below the fixed end, as appropriate.
+
+   Where both ends are free, we move them to the point halfway between them,
+   the white end being one more than the black end.
+-----------------------------------------------------------------------------*/
+    assert(maxval > 0);
+
+    if (reqBvalue < reqWvalue) {
+        /* No overlap; initial attempt is fine. */
+        *nonOlapBvalueP = reqBvalue;
+        *nonOlapWvalueP = reqWvalue;
+    } else {
+        if (bIsFixed && wIsFixed)
+            pm_error("The colors which become black (value <= %u) "
+                     "would overlap the "
+                     "colors which become white (value >= %u).",
+                     reqBvalue, reqWvalue);
+        else if (bIsFixed) {
+            if (reqBvalue >= maxval)
+                pm_error("The black value must be less than the maxval");
+            else {
+                *nonOlapBvalueP = reqBvalue;
+                *nonOlapWvalueP = reqBvalue + 1;
+            }
+        } else if (wIsFixed) {
+            if (reqWvalue == 0)
+                pm_error("The white value must be greater than 0");
+            else {
+                *nonOlapBvalueP = reqWvalue - 1;
+                *nonOlapWvalueP = reqWvalue;
+            }
+        } else {
+            /* Both ends are free; use the point halfway between them. */
+            xelval const midPoint = (reqWvalue + reqBvalue + maxval/2)/2;
+            *nonOlapBvalueP = MIN(midPoint, maxval-1);
+            *nonOlapWvalueP = *nonOlapBvalueP + 1;
+        }
+    }
+}
+
+
+
+static void
+resolvePercentParams(FILE *             const ifP,
+                     unsigned int       const cols,
+                     unsigned int       const rows,
+                     xelval             const maxval,
+                     int                const format,
+                     struct cmdlineInfo const cmdline,
+                     xelval *           const bvalueP,
+                     xelval *           const wvalueP) {
+/*----------------------------------------------------------------------------
+   Figure out the endpoint of the stretch (the value that is to be stretched
+   to black and the one that is to be stretched to white) as requested
+   by the -bvalue, -bpercent, -wvalue, and -wpercent options.
+
+   These values may be invalid due to overlapping, and they may exceed
+   the maximum allowed stretch; Caller must deal with that.
 -----------------------------------------------------------------------------*/
     unsigned int * hist;  /* malloc'ed */
 
@@ -377,45 +442,71 @@ computeEndValues(FILE *             const ifp,
     if (hist == NULL)
         pm_error("Unable to allocate storage for intensity histogram.");
     else {
-        xelval unlimitedBvalue, unlimitedWvalue;
-        unsigned int bLower, wRaise;
-
-        buildHistogram(ifp, cols, rows, maxval, format, hist,
+        buildHistogram(ifP, cols, rows, maxval, format, hist,
                        cmdline.brightMethod);
 
         if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
-            unlimitedBvalue = cmdline.bvalue;
+            *bvalueP = cmdline.bvalue;
         } else {
             xelval percentBvalue;
             computeBottomPercentile(hist, maxval, cols*rows, cmdline.bpercent, 
                                     &percentBvalue);
             if (cmdline.bvalueSpec)
-                unlimitedBvalue = MIN(percentBvalue, cmdline.bvalue);
+                *bvalueP = MIN(percentBvalue, cmdline.bvalue);
             else
-                unlimitedBvalue = percentBvalue;
+                *bvalueP = percentBvalue;
         }
 
         if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
-            unlimitedWvalue = cmdline.wvalue;
+            *wvalueP = cmdline.wvalue;
         } else {
             xelval percentWvalue;
             computeTopPercentile(hist, maxval, cols*rows, cmdline.wpercent, 
                                  &percentWvalue);
             if (cmdline.wvalueSpec)
-                unlimitedWvalue = MAX(percentWvalue, cmdline.wvalue);
+                *wvalueP = MAX(percentWvalue, cmdline.wvalue);
             else
-                unlimitedWvalue = percentWvalue;
+                *wvalueP = percentWvalue;
         }
+        free(hist);
+    }
+}
 
-        computeAdjustmentForExpansionLimit(
-            maxval, unlimitedBvalue, unlimitedWvalue, cmdline.maxExpansion,
-            &bLower, &wRaise);
 
-        *bvalueP = unlimitedBvalue - bLower;
-        *wvalueP = unlimitedWvalue + wRaise;
 
-        free(hist);
-    }
+static void
+computeEndValues(FILE *             const ifP,
+                 int                const cols,
+                 int                const rows,
+                 xelval             const maxval,
+                 int                const format,
+                 struct cmdlineInfo const cmdline,
+                 xelval *           const bvalueP,
+                 xelval *           const wvalueP) {
+/*----------------------------------------------------------------------------
+   Figure out what original values will be translated to full white and
+   full black -- thus defining to what all the other values get translated.
+
+   This may involve looking at the image.  The image is in the file
+   'ifp', which is positioned just past the header (at the raster).
+   Leave it positioned arbitrarily.
+-----------------------------------------------------------------------------*/
+    xelval reqBvalue, reqWvalue, nonOlapBvalue, nonOlapWvalue;
+    unsigned int bLower, wRaise;
+
+    resolvePercentParams(ifP, cols, rows, maxval, format, cmdline,
+                         &reqBvalue, &reqWvalue);
+
+    disOverlap(reqBvalue, reqWvalue,
+               cmdline.bvalueSpec, cmdline.wvalueSpec, maxval,
+               &nonOlapBvalue, &nonOlapWvalue);
+
+    computeAdjustmentForExpansionLimit(
+        maxval, nonOlapBvalue, nonOlapWvalue, cmdline.maxExpansion,
+        &bLower, &wRaise);
+
+    *bvalueP = nonOlapBvalue - bLower;
+    *wvalueP = nonOlapWvalue + wRaise;
 }
 
 
@@ -545,9 +636,9 @@ writeRowNormalized(xel *             const xelrow,
                 float const scaler =
                     brightScaler(p, maxval, newBrightness, brightMethod);
 
-                xelval const r = MIN((int)(PPM_GETR(p)*scaler+0.5), maxval);
-                xelval const g = MIN((int)(PPM_GETG(p)*scaler+0.5), maxval);
-                xelval const b = MIN((int)(PPM_GETB(p)*scaler+0.5), maxval);
+                xelval const r = MIN(ROUNDU(PPM_GETR(p)*scaler), maxval);
+                xelval const g = MIN(ROUNDU(PPM_GETG(p)*scaler), maxval);
+                xelval const b = MIN(ROUNDU(PPM_GETB(p)*scaler), maxval);
                 PNM_ASSIGN(outrow[col], r, g, b);
             } else 
                 PNM_ASSIGN(outrow[col], 
@@ -563,7 +654,7 @@ writeRowNormalized(xel *             const xelrow,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE *ifP;
@@ -572,7 +663,7 @@ main(int argc, char *argv[]) {
     int rows, cols, format;
     xelval bvalue, wvalue;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -584,19 +675,17 @@ main(int argc, char *argv[]) {
 
     computeEndValues(ifP, cols, rows, maxval, format, cmdline, 
                      &bvalue, &wvalue);
-        
-    if (wvalue <= bvalue)
-        pm_error("The colors which become black would overlap the "
-                 "colors which become white.");
-    else {
+    {
         xelval * newBrightness;
         int row;
         xel * xelrow;
         xel * rowbuf;
         
+        assert(wvalue > bvalue);
+
         xelrow = pnm_allocrow(cols);
 
-        pm_message("remapping %d..%d to %d..%d", bvalue, wvalue, 0, maxval);
+        pm_message("remapping %u..%u to %u..%u", bvalue, wvalue, 0, maxval);
 
         computeTransferFunction(bvalue, wvalue, maxval, &newBrightness);
 
@@ -614,7 +703,7 @@ main(int argc, char *argv[]) {
         free(newBrightness);
         pnm_freerow(rowbuf);
         pnm_freerow(xelrow);
-    }
-    pm_close(ifP);
+    } 
+   pm_close(ifP);
     return 0;
 }