From 10b4a4c36092739a5ae9a91c04a67f36cfbb0b53 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Thu, 6 Mar 2014 03:37:21 +0000 Subject: Add -truncate, -clip, -changemaxval git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@2153 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- other/pamfixtrunc.c | 195 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 155 insertions(+), 40 deletions(-) (limited to 'other') 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 @@ -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[]) { } + -- cgit 1.4.1