diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2020-09-26 23:52:08 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2020-09-26 23:52:08 +0000 |
commit | 4c361c0a7638b4f068e8b524e4ff43374a286018 (patch) | |
tree | 0aef192966a1ae996a9f2a0364f3ccbb15d512c7 /editor | |
parent | cd7f17a619a0421a1dfb2ceb09f4af60ead8ad5f (diff) | |
download | netpbm-mirror-4c361c0a7638b4f068e8b524e4ff43374a286018.tar.gz netpbm-mirror-4c361c0a7638b4f068e8b524e4ff43374a286018.tar.xz netpbm-mirror-4c361c0a7638b4f068e8b524e4ff43374a286018.zip |
Promote Development to Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3972 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r-- | editor/Makefile | 4 | ||||
-rw-r--r-- | editor/pamfunc.c | 28 | ||||
-rw-r--r-- | editor/pnmcrop.c | 163 | ||||
-rw-r--r-- | editor/pnmnorm.c | 152 | ||||
-rwxr-xr-x | editor/ppmbrighten | 60 | ||||
-rw-r--r-- | editor/ppmbrighten.c | 218 |
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. -*/ - |