about summary refs log tree commit diff
path: root/other
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2014-03-06 03:37:21 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2014-03-06 03:37:21 +0000
commit10b4a4c36092739a5ae9a91c04a67f36cfbb0b53 (patch)
treee44119076c3180930922bda409346d583fb63e6f /other
parent1e45d82e66be6070805810d1115982c52c9e0dcb (diff)
downloadnetpbm-mirror-10b4a4c36092739a5ae9a91c04a67f36cfbb0b53.tar.gz
netpbm-mirror-10b4a4c36092739a5ae9a91c04a67f36cfbb0b53.tar.xz
netpbm-mirror-10b4a4c36092739a5ae9a91c04a67f36cfbb0b53.zip
Add -truncate, -clip, -changemaxval
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@2153 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'other')
-rw-r--r--other/pamfixtrunc.c195
1 files changed, 155 insertions, 40 deletions
diff --git a/other/pamfixtrunc.c b/other/pamfixtrunc.c
index 8d58b447..8db67e08 100644
--- a/other/pamfixtrunc.c
+++ b/other/pamfixtrunc.c
@@ -1,12 +1,11 @@
 /*============================================================================
-                             pamfixtrunc
+                                 pamfix
 ==============================================================================
-  Fix a Netpbm image that has been truncated, e.g. by I/O error.
+  Salvage a Netpbm image that is corrupted in certain ways.
 
   By Bryan Henderson, January 2007.
 
   Contributed to the public domain by its author.
-
 ============================================================================*/
 
 #include <setjmp.h>
@@ -20,8 +19,11 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char * inputFilespec;  /* Filespec of input file */
+    const char * inputFileName;   /* File name of input file */
     unsigned int verbose;
+    unsigned int truncate;
+    unsigned int changemaxval;
+    unsigned int clip;
 };
 
 
@@ -43,7 +45,10 @@ parseCommandLine(int argc, char ** const argv,
 
     option_def_index = 0;   /* incremented by OPTENTRY */
 
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "verbose",      OPT_FLAG, NULL, &cmdlineP->verbose,      0);
+    OPTENT3(0, "truncate",     OPT_FLAG, NULL, &cmdlineP->truncate,     0);
+    OPTENT3(0, "changemaxval", OPT_FLAG, NULL, &cmdlineP->changemaxval, 0);
+    OPTENT3(0, "clip",         OPT_FLAG, NULL, &cmdlineP->clip,         0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -51,14 +56,18 @@ parseCommandLine(int argc, char ** const argv,
 
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
 
     if (argc-1 == 0) 
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
+
+    if (cmdlineP->changemaxval && cmdlineP->clip)
+        pm_error("You cannot specify both -changemaxval and -clip");
 }
 
 
@@ -66,47 +75,86 @@ parseCommandLine(int argc, char ** const argv,
 static unsigned int readErrRow;
 static bool readErrVerbose;
 
-static pm_usererrormsgfn discardMsg;
+static pm_usererrormsgfn handleRowErrMsg;
 
 static void
-discardMsg(const char * const msg) {
+handleRowErrMsg(const char * const msg) {
     if (readErrVerbose)
         pm_message("Error reading row %u: %s", readErrRow, msg);
 }
 
 
+static sample
+highestSampleInRow(const struct pam * const pamP,
+                   tuple *            const tuplerow) {
+
+    unsigned int col;
+    sample highestSoFar;
+
+    for (col = 0, highestSoFar = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane)
+            highestSoFar = MAX(highestSoFar, tuplerow[col][plane]);
+    }
+    return highestSoFar;
+}
+
+
 
 static void
-countRows(const struct pam * const inpamP,
-          bool               const verbose,
-          unsigned int *     const goodRowCountP) {
+analyzeRaster(const struct pam * const pamP,
+              unsigned int *     const goodRowCountP,
+              sample *           const highestSampleP,
+              bool               const mustAbortOnReadError,
+              bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Go through the raster at which the stream described by *tweakedPamP is
+   presently positioned and count how many rows can be successfully read
+   (including validating the samples against pamP->maxval) and determine the
+   highest sample value in those rows.
 
+   Leave the stream positioned arbitrarily.
+-----------------------------------------------------------------------------*/
     tuple * tuplerow;
     unsigned int row;
     jmp_buf jmpbuf;
     int rc;
-    unsigned int goodRowCount;
     
-    tuplerow = pnm_allocpamrow(inpamP);
+    tuplerow = pnm_allocpamrow(pamP);
 
-    pm_setusererrormsgfn(discardMsg);
+    pm_setusererrormsgfn(handleRowErrMsg);
 
     rc = setjmp(jmpbuf);
     if (rc == 0) {
         pm_setjmpbuf(&jmpbuf);
 
-        readErrVerbose = verbose;
-        goodRowCount = 0;  /* initial value */
-        for (row = 0; row < inpamP->height; ++row) {
+        readErrVerbose  = mustAbortOnReadError || verbose;
+        *goodRowCountP  = 0;  /* initial value */
+        *highestSampleP = 0;  /* initial value */
+
+        for (row = 0; row < pamP->height; ++row) {
             readErrRow = row;
-            pnm_readpamrow(inpamP, tuplerow);
+            pnm_readpamrow(pamP, tuplerow);
             /* The above does not return if it can't read the next row from
                the file.  Instead, it longjmps out of this loop.
+
+               Update return stats now in case the next iteration longjmps out.
             */
-            ++goodRowCount;
+            *highestSampleP =
+                MAX(*highestSampleP,
+                    highestSampleInRow(pamP, tuplerow));
+            ++*goodRowCountP;
+        }
+    } else {
+        /* pnm_readpamrow() encountered an error and longjmped */
+        if (mustAbortOnReadError) {
+            /* handleRowErrMsg() has issued the error message */
+            exit(1);
         }
     }
-    *goodRowCountP = goodRowCount;
+    pm_setjmpbuf(NULL);
+
+    pm_setusererrormsgfn(NULL);
 
     pnm_freepamrow(tuplerow);
 }
@@ -114,26 +162,54 @@ countRows(const struct pam * const inpamP,
 
 
 static void
-copyGoodRows(const struct pam * const inpamP,
-             FILE *             const ofP,
-             unsigned int       const goodRowCount) {
+clipPamRow(const struct pam * const pamP,
+           tuple *            const tuplerow,
+           unsigned int       const row,
+           bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Clip every sample value in tuplerow[] to the maxval.
 
-    struct pam outpam;
-    tuple * tuplerow;
-    unsigned int row;
+   If 'verbose' is true, issue messages about every clipping, indicating it
+   is in row 'row'.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            if (tuplerow[col][plane] > pamP->maxval) {
+                if (verbose) 
+                    pm_message("Clipping: Row %u Col %u Plane %u.  "
+                               "Sample value %lu exceeds the "
+                               "image maxval of %lu",
+                               row, col, plane, tuplerow[col][plane],
+                               pamP->maxval);
+	            tuplerow[col][plane] = pamP->maxval;
+            }
+        }
+    }
+}
 
-    outpam = *inpamP;  /* initial value */
 
-    outpam.file = ofP;
-    outpam.height = goodRowCount;
 
-    tuplerow = pnm_allocpamrow(inpamP);
+static void
+copyGoodRows(const struct pam * const inpamP,
+             const struct pam * const outpamP,
+             bool               const verbose) {
+/*----------------------------------------------------------------------------
+  Copy the raster of the input stream described by *inpamP to the output
+  stream described by *outpamP.  Copy only as many rows as *outpamP allows;
+  assume *outpamP specifies at most the number of rows of input.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
 
-    pnm_writepaminit(&outpam);
+    tuplerow = pnm_allocpamrow(inpamP);
 
-    for (row = 0; row < outpam.height; ++row) {
+    for (row = 0; row < outpamP->height; ++row) {
         pnm_readpamrow(inpamP, tuplerow);
-        pnm_writepamrow(&outpam, tuplerow);
+        clipPamRow(outpamP, tuplerow, row, verbose);
+        pnm_writepamrow(outpamP, tuplerow);
     }
     
     pnm_freepamrow(tuplerow);
@@ -145,28 +221,66 @@ int
 main(int argc, char * argv[]) {
     struct cmdlineInfo cmdline;
     struct pam inpam;
+    struct pam outpam;
+    struct pam tweakedPam;
     FILE * ifP;
     pm_filepos rasterPos;
     unsigned int goodRowCount;
+    sample highestSample;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr_seekable(cmdline.inputFilespec);
+    ifP = pm_openr_seekable(cmdline.inputFileName);
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
     pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
 
-    countRows(&inpam, cmdline.verbose, &goodRowCount);
+    /* Tweak maxval to circumvent out-of-bounds sample value check
+       in libpam.  See function validatePamRow() in libpamread.c .
+
+       Ideally we would like to set tweaked-maxval higher for the
+       plain (text) formats.  We make a compromise to keep things
+       simple.
+    */
+
+    tweakedPam = inpam;  /* initial value */
+
+    if ((cmdline.clip || cmdline.changemaxval)
+        && PNM_FORMAT_TYPE(inpam.format) != PBM_TYPE)
+        tweakedPam.maxval =
+            (((sample) 1) << tweakedPam.bytes_per_sample * 8) - 1;
+
+    analyzeRaster(&tweakedPam, &goodRowCount, &highestSample,
+                  !cmdline.truncate, cmdline.verbose);
+
+    if (goodRowCount == 0)
+        pm_error("Cannot read a single row from the image%s",
+                 cmdline.verbose ? "" : ".  Use -verbose to find out why");
+
+    if (goodRowCount < inpam.height)
+        pm_message("Copying %u good rows; %u bottom rows missing%s",
+                   goodRowCount, inpam.height - goodRowCount,
+                   cmdline.verbose ? "" : ".  Use -verbose to find out why");
 
-    pm_message("Copying %u good rows; %u bottom rows missing",
-               goodRowCount, inpam.height - goodRowCount);
-    
     pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
 
-    copyGoodRows(&inpam, stdout, goodRowCount);
+    outpam = inpam;  /* initial value */
+
+    outpam.file = stdout;
+    outpam.height = goodRowCount;
+    if (cmdline.changemaxval && highestSample > outpam.maxval) {
+        pm_message("Raising maxval from %lu to %lu to legitimize "
+                   "all sample values",
+                    outpam.maxval, highestSample);
+        outpam.maxval = highestSample;
+    }
+
+    pnm_writepaminit(&outpam);
+
+    copyGoodRows(&tweakedPam, &outpam, cmdline.verbose);
 
     pm_close(inpam.file);
     
@@ -174,3 +288,4 @@ main(int argc, char * argv[]) {
 }
 
 
+