diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2023-12-28 19:53:34 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2023-12-28 19:53:34 +0000 |
commit | 5d16663331afd0bc2edaeb2e49042dc219ce9c2f (patch) | |
tree | 476fbb2ab4311d4bb6d65b250825e254a7a2c1ef /editor | |
parent | 42f0bf8e7f1ff88000a3584c265e6f1631662ec4 (diff) | |
download | netpbm-mirror-5d16663331afd0bc2edaeb2e49042dc219ce9c2f.tar.gz netpbm-mirror-5d16663331afd0bc2edaeb2e49042dc219ce9c2f.tar.xz netpbm-mirror-5d16663331afd0bc2edaeb2e49042dc219ce9c2f.zip |
promote Development to Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@4827 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r-- | editor/Makefile | 2 | ||||
-rw-r--r-- | editor/pnmhisteq.c | 25 | ||||
-rwxr-xr-x | editor/pnmmargin | 31 | ||||
-rw-r--r-- | editor/pnmpad.c | 810 | ||||
-rw-r--r-- | editor/pnmremap.c | 48 | ||||
-rw-r--r-- | editor/pnmscalefixed.c | 7 | ||||
-rw-r--r-- | editor/ppmdither.c | 96 | ||||
-rw-r--r-- | editor/specialty/pamdeinterlace.c | 27 | ||||
-rw-r--r-- | editor/specialty/pamoil.c | 233 | ||||
-rw-r--r-- | editor/specialty/pnmindex.c | 338 |
10 files changed, 1126 insertions, 491 deletions
diff --git a/editor/Makefile b/editor/Makefile index 0027832c..5929fc71 100644 --- a/editor/Makefile +++ b/editor/Makefile @@ -121,5 +121,5 @@ mergecomptrylist: echo "TRY(\"pnmcut\", main_pamcut);" >>$@ echo "TRY(\"pnmscale\", main_pamscale);" >>$@ echo "TRY(\"pnmcomp\", main_pamcomp);" >>$@ - echo "TRY(\"pnmcat\", main_pamcomp);" >>$@ + echo "TRY(\"pnmcat\", main_pamcat);" >>$@ diff --git a/editor/pnmhisteq.c b/editor/pnmhisteq.c index a339f73f..c2e39089 100644 --- a/editor/pnmhisteq.c +++ b/editor/pnmhisteq.c @@ -1,15 +1,14 @@ -/* - pnmhisteq.c - - Equalize histogram for a PNM image +/*============================================================================= + pnmhisteq +=============================================================================== + Equalize histogram for a PNM image By Bryan Henderson 2005.09.10, based on ideas from the program of the same name by John Walker (kelvin@fourmilab.ch) -- March MVM. WWW home page: http://www.fourmilab.ch/ in 1995. This program is contributed to the public domain by its author. -*/ - +=============================================================================*/ #include <string.h> #include "pm_c_util.h" @@ -18,7 +17,7 @@ #include "mallocvar.h" -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -34,8 +33,8 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo * const cmdlineP) { +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. @@ -68,7 +67,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ @@ -497,9 +496,9 @@ writeMap(const char * const wmapFileName, int -main(int argc, char * argv[]) { +main(int argc, const char ** const argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP; gray * lumamap; /* Luminosity map */ unsigned int * lumahist; /* Histogram of luminosity values */ @@ -509,7 +508,7 @@ main(int argc, char * argv[]) { xel ** xels; /* Pixel array */ unsigned int pixelCount; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/editor/pnmmargin b/editor/pnmmargin index 94321c7f..6f82ddbe 100755 --- a/editor/pnmmargin +++ b/editor/pnmmargin @@ -11,15 +11,6 @@ # documentation. This software is provided "as is" without express or # implied warranty. -tempdir=$(mktemp -d "${TMPDIR:-/tmp}/netpbm.XXXXXXXX") || - { echo "Could not create temporary file. Exiting." 1>&2; exit 1; } -trap 'rm -rf $tempdir' 0 1 3 15 - -tmp1=$tempdir/pnmm1 -tmp2=$tempdir/pnmm2 -tmp3=$tempdir/pnmm3 -tmp4=$tempdir/pnmm4 - color="-gofigure" plainopt="" @@ -79,37 +70,31 @@ if [ ${2-""} ] ; then exit 1 fi -# Capture input file in a tmp file, in case it's a pipe. # TODO: This code does not consider the case when the input file is specified # and there is also input coming in from a pipe. -cat $@ > $tmp1 - if [ $size -eq 0 ] ; then # Zero margin; just copy input to output - pamtopnm $plainopt $tmp1; + pamtopnm $plainopt $@; else # If -white or -black, write output with pnmpad and exit. # Otherwise construct spacer files. case "$color" in -gofigure ) - pamcut 0 0 1 1 $tmp1 | pnmtile $size 1 > $tmp2 + pnmpad $plainopt -detect-background \ + -left=$size -right=$size -top=$size -bottom=$size $@ + exit ;; -white | -black ) pnmpad $plainopt $color \ - -left=$size -right=$size -top=$size -bottom=$size $tmp1 + -left=$size -right=$size -top=$size -bottom=$size $@ exit ;; * ) - ppmmake $color $size 1 > $tmp2 + pnmpad $plainopt -color $color \ + -left=$size -right=$size -top=$size -bottom=$size $@ + exit ;; esac - pamflip -rotate90 $tmp2 > $tmp3 - - # Cat things together. - pamcat -leftright $tmp2 $tmp1 $tmp2 > $tmp4 - pamcat -topbottom $plainopt $tmp3 $tmp4 $tmp3 fi - - diff --git a/editor/pnmpad.c b/editor/pnmpad.c index 10b69d03..94ea0ed1 100644 --- a/editor/pnmpad.c +++ b/editor/pnmpad.c @@ -1,13 +1,15 @@ -/* pnmpad.c - add border to sides of a portable anymap +/* pnmpad.c - add border to sides of a PNM ** AJCD 4/9/90 */ +#include <stdbool.h> #include <assert.h> #include <string.h> #include <stdio.h> #include "pm_c_util.h" #include "mallocvar.h" +#include <nstring.h> #include "shhopt.h" #include "pnm.h" @@ -16,11 +18,54 @@ arithmetic overflow */ -struct cmdlineInfo { +enum PadType { + /* PAD_COLOR means the specified padding is not black, white, or gray -- + even regardless of whether user specified it with a -color option. + E.g. -color=black is PAD_BLACK. + */ + PAD_BLACK, + PAD_WHITE, + PAD_GRAY, + PAD_COLOR, + PAD_DETECTED_BG, + PAD_EXTEND_EDGE +}; + +enum PromoteType { + PROMOTE_NONE, + PROMOTE_FORMAT, + PROMOTE_ALL +}; + + + +static enum PadType +padTypeFmColorStr(const char * const colorStr) { + + pixel const color = ppm_parsecolor(colorStr, PPM_OVERALLMAXVAL); + + enum PadType retval; + + if (PPM_ISGRAY(color)) { + if (PPM_GETR(color) == 0) + retval = PAD_BLACK; + else if (PPM_GETR(color) == PPM_OVERALLMAXVAL) + retval = PAD_WHITE; + else + retval = PAD_GRAY; + } else + retval = PAD_COLOR; + + return retval; +} + + + +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char * input_filespec; /* Filespecs of input files */ + const char * inputFileNm; /* Names of input files */ unsigned int xsize; unsigned int xsizeSpec; unsigned int ysize; @@ -37,7 +82,9 @@ struct cmdlineInfo { float yalign; unsigned int mwidth; unsigned int mheight; - unsigned int white; /* >0: pad white; 0: pad black */ + enum PadType padType; + const char * color; + enum PromoteType promote; unsigned int reportonly; unsigned int verbose; }; @@ -45,8 +92,45 @@ struct cmdlineInfo { static void +validateNoOldOptionSyntax( int const argc, const char ** const argv) { +/*---------------------------------------------------------------------------- + Reject obsolete command line syntax, e.g. "pnmpad -l50". + + Starting in Netpbm 9.25 (February 2002), this resulted in a warning message + and was no longer documented. Starting in Netpbm 11.05 (December 2023), + it is no longer accepted. + + It was too hard to maintain. +-----------------------------------------------------------------------------*/ + bool isOld; + + isOld = false; /* initial assumption */ + + if (argc > 1 && argv[1][0] == '-') { + if (argv[1][1] == 't' || argv[1][1] == 'b' + || argv[1][1] == 'l' || argv[1][1] == 'r') { + if (argv[1][2] >= '0' && argv[1][2] <= '9') + isOld = true; + } + } + if (argc > 2 && argv[2][0] == '-') { + if (argv[2][1] == 't' || argv[2][1] == 'b' + || argv[2][1] == 'l' || argv[2][1] == 'r') { + if (argv[2][2] >= '0' && argv[2][2] <= '9') + isOld = true; + } + } + if (isOld) + pm_error("Old-style Unix options (e.g. \"-l50\") " + "not accepted by current 'pnmpad'. " + "Use e.g. \"-l 50\" instead"); +} + + + +static void parseCommandLine(int argc, const char ** argv, - struct cmdlineInfo * const cmdlineP) { + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. @@ -57,8 +141,12 @@ parseCommandLine(int argc, const char ** argv, optStruct3 opt; unsigned int option_def_index; - unsigned int blackOpt; + unsigned int black, white, extend_edge; unsigned int xalignSpec, yalignSpec, mwidthSpec, mheightSpec; + unsigned int colorSpec; + unsigned int detect_background; + unsigned int promoteSpec; + const char * promoteOpt; MALLOCARRAY_NOFAIL(option_def, 100); @@ -88,28 +176,89 @@ parseCommandLine(int argc, const char ** argv, OPTENT3(0, "valign", OPT_FLOAT, &cmdlineP->yalign, &yalignSpec, 0); OPTENT3(0, "mwidth", OPT_UINT, &cmdlineP->mwidth, - &mwidthSpec, 0); + &mwidthSpec, 0); OPTENT3(0, "mheight", OPT_UINT, &cmdlineP->mheight, - &mheightSpec, 0); + &mheightSpec, 0); OPTENT3(0, "black", OPT_FLAG, NULL, - &blackOpt, 0); + &black, 0); OPTENT3(0, "white", OPT_FLAG, NULL, - &cmdlineP->white, 0); + &white, 0); + OPTENT3(0, "color", OPT_STRING, &cmdlineP->color, + &colorSpec, 0); + OPTENT3(0, "extend-edge", OPT_FLAG, NULL, + &extend_edge, 0); + OPTENT3(0, "detect-background", OPT_FLAG, NULL, + &detect_background, 0); + OPTENT3(0, "promote", OPT_STRING, &promoteOpt, + &promoteSpec, 0); OPTENT3(0, "reportonly", OPT_FLAG, NULL, - &cmdlineP->reportonly, 0); - OPTENT3(0, "verbose", OPT_FLAG, NULL, - &cmdlineP->verbose, 0); + &cmdlineP->reportonly, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 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 */ + 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 (blackOpt && cmdlineP->white) - pm_error("You cannot specify both -black and -white"); + if (!colorSpec) + cmdlineP->color = NULL; + + if (black + white + colorSpec + detect_background + extend_edge > 1) + pm_error("You can specify only one one of -black, -white, " + "-color -detect-background and -extend-edge"); + else if (black) + cmdlineP->padType = PAD_BLACK; + else if (white) + cmdlineP->padType = PAD_WHITE; + else if (colorSpec) + cmdlineP->padType = padTypeFmColorStr(cmdlineP->color); + else if (extend_edge) + cmdlineP->padType = PAD_EXTEND_EDGE; + else if (detect_background) + cmdlineP->padType = PAD_DETECTED_BG; + else { + cmdlineP->padType = PAD_BLACK; + if (cmdlineP->verbose) + pm_message("No pad color specified. Padding with black."); + } + if (promoteSpec) { + if (!colorSpec) + pm_error("-promote requires -color"); + else { + switch (cmdlineP->padType) { + case PAD_COLOR: + case PAD_GRAY: + if (streq(promoteOpt, "none")) + cmdlineP->promote = PROMOTE_NONE; + else if (streq(promoteOpt, "format")) + cmdlineP->promote = PROMOTE_FORMAT; + else if (streq(promoteOpt, "all")) + cmdlineP->promote = PROMOTE_ALL; + else + pm_error("Invalid value for -promote. " + "Valid values are 'none', 'format', and 'all'"); + break; + case PAD_BLACK: + case PAD_WHITE: + cmdlineP->promote = PROMOTE_NONE; + break; + case PAD_DETECTED_BG: + case PAD_EXTEND_EDGE: + assert(false); /* Because we have -color */ + break; + } + } + } else { + if (cmdlineP->padType == PAD_COLOR || + cmdlineP->padType == PAD_GRAY) { + cmdlineP->promote = PROMOTE_ALL; + } else + cmdlineP->promote = PROMOTE_NONE; + } if (cmdlineP->topSpec > 1) pm_error("You can specify -top only once"); @@ -171,87 +320,15 @@ parseCommandLine(int argc, const char ** argv, pm_error("This program takes at most 1 parameter. You specified %d", argc-1); else if (argc-1 == 1) - cmdlineP->input_filespec = argv[1]; + cmdlineP->inputFileNm = argv[1]; else - cmdlineP->input_filespec = "-"; + cmdlineP->inputFileNm = "-"; } static void -parseCommandLineOld(int argc, const char ** argv, - struct cmdlineInfo * const cmdlineP) { - - /* This syntax was abandoned in February 2002. */ - pm_message("Warning: old style options are deprecated!"); - - cmdlineP->xsize = cmdlineP->ysize = 0; - cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = 0; - cmdlineP->xalign = cmdlineP->yalign = 0.5; - cmdlineP->white = cmdlineP->verbose = FALSE; - cmdlineP->reportonly = FALSE; - cmdlineP->topSpec = cmdlineP->bottomSpec = - cmdlineP->leftSpec = cmdlineP->rightSpec = TRUE; - cmdlineP->mwidth = cmdlineP->mheight = 1; - - while (argc >= 2 && argv[1][0] == '-') { - if (strcmp(argv[1]+1,"black") == 0) cmdlineP->white = FALSE; - else if (strcmp(argv[1]+1,"white") == 0) cmdlineP->white = TRUE; - else switch (argv[1][1]) { - case 'l': - if (atoi(argv[1]+2) < 0) - pm_error("left border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) - pm_error("left border too large"); - else - cmdlineP->left = atoi(argv[1]+2); - break; - case 'r': - if (atoi(argv[1]+2) < 0) - pm_error("right border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) - pm_error("right border too large"); - else - cmdlineP->right = atoi(argv[1]+2); - break; - case 'b': - if (atoi(argv[1]+2) < 0) - pm_error("bottom border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) - pm_error("bottom border too large"); - else - cmdlineP->bottom = atoi(argv[1]+2); - break; - case 't': - if (atoi(argv[1]+2) < 0) - pm_error("top border too small"); - else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT) - pm_error("top border too large"); - else - cmdlineP->top = atoi(argv[1]+2); - break; - default: - pm_usage("[-white|-black] [-l#] [-r#] [-t#] [-b#] [pnmfile]"); - } - argc--, argv++; - } - - cmdlineP->xsizeSpec = (cmdlineP->xsize > 0); - cmdlineP->ysizeSpec = (cmdlineP->ysize > 0); - - if (argc > 2) - pm_usage("[-white|-black] [-l#] [-r#] [-t#] [-b#] [pnmfile]"); - - if (argc == 2) - cmdlineP->input_filespec = argv[1]; - else - cmdlineP->input_filespec = "-"; -} - - - -static void -validateHorizontalSize(struct cmdlineInfo const cmdline, +validateHorizontalSize(struct CmdlineInfo const cmdline, unsigned int const cols) { /*---------------------------------------------------------------------------- Abort the program if the padding parameters in 'cmdline', applied to @@ -279,7 +356,7 @@ validateHorizontalSize(struct cmdlineInfo const cmdline, "for this program to compute"); if (cmdline.xsizeSpec && - (double) cmdline.xsize + (double) mwidthMaxPad> MAX_WIDTHHEIGHT) + (double) cmdline.xsize + (double) mwidthMaxPad > MAX_WIDTHHEIGHT) pm_error("Given padding parameters make output width too large " "for this program to compute"); } @@ -400,7 +477,7 @@ computePadSizesOneDim(unsigned int const unpaddedSize, static void -computeHorizontalPadSizes(struct cmdlineInfo const cmdline, +computeHorizontalPadSizes(struct CmdlineInfo const cmdline, unsigned int const cols, unsigned int * const lpadP, unsigned int * const rpadP) { @@ -419,7 +496,7 @@ computeHorizontalPadSizes(struct cmdlineInfo const cmdline, static void -validateVerticalSize(struct cmdlineInfo const cmdline, +validateVerticalSize(struct CmdlineInfo const cmdline, unsigned int const rows) { /*---------------------------------------------------------------------------- Abort the program if the padding parameters in 'cmdline', applied to @@ -457,7 +534,7 @@ validateVerticalSize(struct cmdlineInfo const cmdline, static void -computeVerticalPadSizes(struct cmdlineInfo const cmdline, +computeVerticalPadSizes(struct CmdlineInfo const cmdline, int const rows, unsigned int * const tpadP, unsigned int * const bpadP) { @@ -476,7 +553,7 @@ computeVerticalPadSizes(struct cmdlineInfo const cmdline, static void -computePadSizes(struct cmdlineInfo const cmdline, +computePadSizes(struct CmdlineInfo const cmdline, int const cols, int const rows, unsigned int * const lpadP, @@ -512,6 +589,205 @@ reportPadSizes(int const inCols, +static unsigned char +bitPeek(const unsigned char * const bitrow, + unsigned int const position) { +/*---------------------------------------------------------------------------- + Value of pixel (bit) of 'bitrow' at 'position'. +-----------------------------------------------------------------------------*/ + bool retval; + + unsigned int const charPosition = position / 8; + unsigned int const bitPosition = position % 8; + + retval = (bitrow[charPosition] >> (7 - bitPosition)) & 0x01; + + return retval; +} + + + +static void +extendLeftPbm(unsigned char * const bitrow, + unsigned int const lpad) { +/*---------------------------------------------------------------------------- + Add 'lpad' pixels of padding the the left side of PBM row 'bitrow' by + duplicating leftmost pixel. +-----------------------------------------------------------------------------*/ + unsigned int const padCharCt = lpad / 8; + unsigned int const fractBitCt = lpad % 8; + unsigned int const color = bitPeek(bitrow, lpad); + + unsigned int colChar; + + for (colChar = 0; colChar < padCharCt; ++colChar) + bitrow[colChar] = 0xff * color; + + if (fractBitCt > 0) { + bitrow[colChar] = (unsigned char) + (0xff * color) << (8-fractBitCt) | + (bitrow[colChar] & (0xff >> fractBitCt)); + } +} + + + +static void +extendRightPbm(unsigned char * const bitrow, + unsigned int const lcols, + unsigned int const rpad) { +/*---------------------------------------------------------------------------- + Add 'rpad' pixels of padding the the left side of PBM row 'bitrow', + which is 'lcols' columns wide, by duplicating leftmost pixel. +-----------------------------------------------------------------------------*/ + unsigned int const rpad0 = (8 - lcols % 8) % 8; + unsigned int const rpad1 = rpad - rpad0; + unsigned int const rpad1CharCt = + rpad1 > 0 ? pbm_packed_bytes(rpad1) : 0; + unsigned int const lastColChar = lcols / 8 - (rpad0 == 0 ? 1 : 0); + unsigned char const fillColor = bitPeek(bitrow, lcols - 1); + + unsigned int colChar; + + if (rpad0 > 0) { + if (fillColor == 0x1) { + bitrow[lastColChar] = + bitrow[lastColChar] | (bitrow[lastColChar] - 1); + } else if (lcols % 8 > 0) { + /* This is essentially pbm_cleanrowend_packed(bitrow, lcols); */ + + unsigned int const last = pbm_packed_bytes(lcols) - 1; + + bitrow[last] >>= 8 - lcols % 8; + bitrow[last] <<= 8 - lcols % 8; + } + } + + for (colChar = 0; colChar < rpad1CharCt; ++colChar) + bitrow[lastColChar + colChar +1] = 0xff * fillColor; + + if (fillColor == 0x1) + pbm_cleanrowend_packed(bitrow, lcols + rpad); +} + + + +static void +extendEdgePbm(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + int const format, + unsigned int const newcols, + unsigned int const lpad, + unsigned int const rpad, + unsigned int const tpad, + unsigned int const bpad, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Fast extend-edge routine for PBM +-----------------------------------------------------------------------------*/ + unsigned char * const newbitrow = pbm_allocrow_packed(newcols); + + unsigned int row; + + pbm_writepbminit(stdout, newcols, rows + tpad + bpad, 0); + + /* Write top row tpad + 1 times */ + if (rpad > 0) + newbitrow[(cols + lpad) / 8] = 0x00; + + pbm_readpbmrow_bitoffset(ifP, newbitrow, cols, format, lpad); + + if (lpad > 0) + extendLeftPbm(newbitrow, lpad); + if (rpad > 0) + extendRightPbm(newbitrow, lpad + cols, rpad); + + pbm_cleanrowend_packed(newbitrow, newcols); + + for (row = 0; row < tpad + 1; ++row) + pbm_writepbmrow_packed(ofP, newbitrow, newcols, 0); + + /* Read rows, shift and write with left and right margins added. + + Alter left/right margin only when the color of leftmost/rightmost + pixel is different from that of the previous row. + */ + + for (row = 1; row < rows; ++row) { + pbm_readpbmrow_bitoffset(ifP, newbitrow, cols, format, lpad); + + if (lpad > 0 && + (bitPeek(newbitrow, lpad - 1) != bitPeek(newbitrow, lpad))) + extendLeftPbm(newbitrow, lpad); + if (rpad > 0 && + (bitPeek(newbitrow, lpad + cols -1) != + bitPeek(newbitrow, lpad + cols))) + extendRightPbm(newbitrow, lpad + cols, rpad); + pbm_writepbmrow_packed(ofP, newbitrow, newcols, 0); + } + + /* Write bottom margin */ + for (row = 0; row < bpad; ++row) + pbm_writepbmrow_packed(ofP, newbitrow, newcols, 0); + + pnm_freerow(newbitrow); +} + + + +static void +extendEdgeGeneral(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + unsigned int const newcols, + unsigned int const lpad, + unsigned int const rpad, + unsigned int const tpad, + unsigned int const bpad, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + General extend-edge routine (logic works for PBM) +-----------------------------------------------------------------------------*/ + xel * const xelrow = pnm_allocrow(newcols); + + unsigned int row, col; + + pnm_writepnminit(ofP, newcols, rows + tpad + bpad, maxval, format, 0); + + pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format); + + for (col = 0; col < lpad; ++col) + xelrow[col] = xelrow[lpad]; + for (col = lpad + cols; col < newcols; ++col) + xelrow[col] = xelrow[lpad + cols - 1]; + + for (row = 0; row < tpad + 1; ++row) + pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0); + + for (row = 1; row < rows; ++row) { + unsigned int col; + + pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format); + + for (col = 0; col < lpad; ++col) + xelrow[col] = xelrow[lpad]; + for (col = lpad + cols; col < newcols; ++col) + xelrow[col] = xelrow[lpad + cols - 1]; + + pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0); + } + + for (row = 0; row < bpad; ++row) + pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0); + + pnm_freerow(xelrow); +} + + + static void padPbm(FILE * const ifP, unsigned int const cols, @@ -522,7 +798,8 @@ padPbm(FILE * const ifP, unsigned int const rpad, unsigned int const tpad, unsigned int const bpad, - bool const colorWhite) { + enum PadType const padType, + FILE * const ofP) { /*---------------------------------------------------------------------------- Fast padding routine for PBM -----------------------------------------------------------------------------*/ @@ -530,7 +807,7 @@ padPbm(FILE * const ifP, unsigned char * const newrow = pbm_allocrow_packed(newcols); unsigned char const padChar = - 0xff * (colorWhite ? PBM_WHITE : PBM_BLACK); + 0xff * (padType == PAD_WHITE ? PBM_WHITE : PBM_BLACK); unsigned int const newColChars = pbm_packed_bytes(newcols); @@ -546,70 +823,280 @@ padPbm(FILE * const ifP, newrow[newColChars-1] <<= 8 - newcols % 8; } - pbm_writepbminit(stdout, newcols, rows + tpad + bpad, 0); + pbm_writepbminit(ofP, newcols, rows + tpad + bpad, 0); /* Write top margin */ for (row = 0; row < tpad; ++row) - pbm_writepbmrow_packed(stdout, bgrow, newcols, 0); + pbm_writepbmrow_packed(ofP, bgrow, newcols, 0); /* Read rows, shift and write with left and right margins added */ for (row = 0; row < rows; ++row) { pbm_readpbmrow_bitoffset(ifP, newrow, cols, format, lpad); - pbm_writepbmrow_packed(stdout, newrow, newcols, 0); + pbm_writepbmrow_packed(ofP, newrow, newcols, 0); } pnm_freerow(newrow); /* Write bottom margin */ for (row = 0; row < bpad; ++row) - pbm_writepbmrow_packed(stdout, bgrow, newcols, 0); + pbm_writepbmrow_packed(ofP, bgrow, newcols, 0); pnm_freerow(bgrow); } + +static xel +regressColor(int const format, + int const maxval, + xel const color) { + + gray const grayval = (gray) ppm_luminosity(color); + + xel retval; + + switch (PNM_FORMAT_TYPE(format)) { + case PBM_TYPE: + { + gray const threshold = maxval / 2; + + retval = grayval > threshold ? + pnm_whitexel(maxval, format) : pnm_blackxel(maxval, format); + } + break; + case PGM_TYPE: + retval.b = grayval; + retval.r = retval.g = 0; + break; + case PPM_TYPE: + retval = color; + break; + default: + pm_error("internal error -- impossible value of PNM format " + "type in regressColor"); + } + return retval; +} + + + +static void +computeOutputFormatAndBackground(int const format, + int const maxval, + enum PadType const padType, + const char * const colorStr, + enum PromoteType const promoteType, + int * const newformatP, + pixval * const newmaxvalP, + xel * const backgroundP) { + + int newformat; + xelval newmaxval; + xel background; + + switch (promoteType) { + case PROMOTE_NONE: + newformat = format; + newmaxval = maxval; + + switch (padType) { + case PAD_BLACK: + background = pnm_blackxel(newmaxval, newformat); + break; + case PAD_WHITE: + background = pnm_whitexel(newmaxval, newformat); + break; + case PAD_GRAY: + case PAD_COLOR: + background = regressColor(format, maxval, + ppm_parsecolor(colorStr, newmaxval)); + break; + case PAD_DETECTED_BG: + case PAD_EXTEND_EDGE: + pm_error("internal error -- impossible value of 'promoteType' " + "in computeOutputFormatAndBackground"); + } break; + + case PROMOTE_FORMAT: + case PROMOTE_ALL: { + newmaxval = promoteType == PROMOTE_ALL ? MAX(255, maxval) : maxval; + + switch (padType) { + case PAD_BLACK: + newformat = format; + background = pnm_blackxel(newmaxval, newformat); + break; + case PAD_WHITE: + newformat = format; + background = pnm_whitexel(newmaxval, newformat); + break; + case PAD_GRAY: + newformat = + PNM_FORMAT_TYPE(format) == PBM_TYPE ? PGM_TYPE : format; + background = ppm_parsecolor(colorStr, newmaxval); + break; + case PAD_COLOR: + newformat = PPM_TYPE; + background = ppm_parsecolor(colorStr, newmaxval); + break; + case PAD_DETECTED_BG: + case PAD_EXTEND_EDGE: + pm_error("internal error -- impossible value of 'promoteType' " + "in computeOutputFormatAndBackground"); + } break; + } + } + + *newformatP = newformat; + *newmaxvalP = newmaxval; + *backgroundP = background; +} + + + static void -padGeneral(FILE * const ifP, - unsigned int const cols, - unsigned int const rows, - xelval const maxval, - int const format, - unsigned int const newcols, - unsigned int const lpad, - unsigned int const rpad, - unsigned int const tpad, - unsigned int const bpad, - bool const colorWhite) { +padDetectedBg(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + unsigned int const newcols, + unsigned int const lpad, + unsigned int const rpad, + unsigned int const tpad, + unsigned int const bpad, + FILE * const ofP) { /*---------------------------------------------------------------------------- - General padding routine (logic works for PBM) + Pad using top left pixel as background color. + + (There is no special PBM routine for this case) -----------------------------------------------------------------------------*/ xel * const bgrow = pnm_allocrow(newcols); + /* A row of background color (for top and bottom padding) */ xel * const xelrow = pnm_allocrow(newcols); - xel background; + /* A row of padded output image. Left and right padding is + constant; middle varies from row to row. + */ + xel * const window = &xelrow[lpad]; + /* The foreground part of the current row */ + + unsigned int row;; + + pnm_writepnminit(ofP, newcols, rows + tpad + bpad, maxval, format, 0); + + pnm_readpnmrow(ifP, window, cols, maxval, format); + + { + /* Set 'bgrow' and constant parts of 'xelrow' */ + xel const background = window[0]; + + unsigned int col; + + for (col = 0; col < newcols; ++col) + bgrow[col] = background; + + for (col = 0; col < lpad; ++col) + xelrow[col] = background; + + for (col = lpad + cols; col < newcols; ++col) + xelrow[col] = background; + } + + /* Write top padding */ + for (row = 0; row < tpad; ++row) + pnm_writepnmrow(ofP, bgrow, newcols, maxval, format, 0); + + /* Write body of image */ + pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0); + + for (row = 1; row < rows; ++row) { + pnm_readpnmrow(ifP, window, cols, maxval, format); + pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0); + } + pnm_freerow(xelrow); + + /* Write bottom padding */ + for (row = 0; row < bpad; ++row) + pnm_writepnmrow(ofP, bgrow, newcols, maxval, format, 0); + + pnm_freerow(bgrow); +} + + + +static void +padGeneral(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + unsigned int const newcols, + unsigned int const lpad, + unsigned int const rpad, + unsigned int const tpad, + unsigned int const bpad, + enum PadType const padType, + const char * const colorStr, + enum PromoteType const promoteType, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + General padding routine (logic works for PBM) +-----------------------------------------------------------------------------*/ + xel * const bgrow = pnm_allocrow(newcols); + /* A row of background color (for top and bottom padding) */ + + int newformat; + pixval newmaxval; + xel background; + unsigned int row, col; - if (colorWhite) - background = pnm_whitexel(maxval, format); - else - background = pnm_blackxel(maxval, format); + computeOutputFormatAndBackground( + format, maxval, padType, colorStr, promoteType, + &newformat, &newmaxval, &background); + + if (newformat != format) + pm_message("Promoting to %s", pnm_formattypenm(newformat)); for (col = 0; col < newcols; ++col) - xelrow[col] = bgrow[col] = background; + bgrow[col] = background; - pnm_writepnminit(stdout, newcols, rows + tpad + bpad, maxval, format, 0); + pnm_writepnminit(ofP, newcols, rows + tpad + bpad, + newmaxval, newformat, 0); + + /* Write top padding */ for (row = 0; row < tpad; ++row) - pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0); + pnm_writepnmrow(ofP, bgrow, newcols, newmaxval, newformat, 0); - for (row = 0; row < rows; ++row) { - pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format); - pnm_writepnmrow(stdout, xelrow, newcols, maxval, format, 0); + /* Write body of image */ + { + xel * const xelrow = pnm_allocrow(newcols); + /* A row of padded output image. Left and right padding is + constant; middle varies from row to row. + */ + xel * const window = &xelrow[lpad]; + /* The foreground part of the current row */ + + /* Initial value for 'xelrow': all background */ + for (col = 0; col < newcols; ++col) + xelrow[col] = bgrow[col] = background; + + for (row = 0; row < rows; ++row) { + pnm_readpnmrow(ifP, window, cols, maxval, format); + if (maxval != newmaxval || format != newformat) { + pnm_promoteformatrow(window, cols, maxval, format, + newmaxval, newformat); + } + pnm_writepnmrow(ofP, xelrow, newcols, newmaxval, newformat, 0); + } + pnm_freerow(xelrow); } + /* Write bottom padding */ for (row = 0; row < bpad; ++row) - pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0); + pnm_writepnmrow(ofP, bgrow, newcols, newmaxval, newformat, 0); - pnm_freerow(xelrow); pnm_freerow(bgrow); } @@ -618,43 +1105,25 @@ padGeneral(FILE * const ifP, int main(int argc, const char ** argv) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP; xelval maxval; int rows, cols, newcols, format; - bool depr_cmd; /* use deprecated commandline interface */ unsigned int lpad, rpad, tpad, bpad; pm_proginit(&argc, argv); - /* detect deprecated options */ - depr_cmd = FALSE; /* initial assumption */ - if (argc > 1 && argv[1][0] == '-') { - if (argv[1][1] == 't' || argv[1][1] == 'b' - || argv[1][1] == 'l' || argv[1][1] == 'r') { - if (argv[1][2] >= '0' && argv[1][2] <= '9') - depr_cmd = TRUE; - } - } - if (argc > 2 && argv[2][0] == '-') { - if (argv[2][1] == 't' || argv[2][1] == 'b' - || argv[2][1] == 'l' || argv[2][1] == 'r') { - if (argv[2][2] >= '0' && argv[2][2] <= '9') - depr_cmd = TRUE; - } - } + validateNoOldOptionSyntax(argc, argv); - if (depr_cmd) - parseCommandLineOld(argc, argv, &cmdline); - else - parseCommandLine(argc, argv, &cmdline); + parseCommandLine(argc, argv, &cmdline); - ifP = pm_openr(cmdline.input_filespec); + ifP = pm_openr(cmdline.inputFileNm); pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); - if (cmdline.verbose) pm_message("image WxH = %dx%d", cols, rows); + if (cmdline.verbose) + pm_message("image WxH = %dx%d", cols, rows); computePadSizes(cmdline, cols, rows, &lpad, &rpad, &tpad, &bpad); @@ -663,15 +1132,40 @@ main(int argc, const char ** argv) { if (cmdline.reportonly) reportPadSizes(cols, rows, lpad, rpad, tpad, bpad); else { - if (PNM_FORMAT_TYPE(format) == PBM_TYPE) - padPbm(ifP, cols, rows, format, newcols, lpad, rpad, tpad, bpad, - !!cmdline.white); - else - padGeneral(ifP, cols, rows, maxval, format, - newcols, lpad, rpad, tpad, bpad, !!cmdline.white); + switch (cmdline.padType) { + case PAD_EXTEND_EDGE: + if (PNM_FORMAT_TYPE(format) == PBM_TYPE) + extendEdgePbm(ifP, cols, rows, format, + newcols, lpad, rpad, tpad, bpad, stdout); + else + extendEdgeGeneral(ifP, cols, rows, maxval, format, + newcols, lpad, rpad, tpad, bpad, stdout); + break; + case PAD_DETECTED_BG: + padDetectedBg(ifP, cols, rows, maxval, format, + newcols, lpad, rpad, tpad, bpad, stdout); + break; + case PAD_BLACK: + case PAD_WHITE: + case PAD_GRAY: + case PAD_COLOR: + if (PNM_FORMAT_TYPE(format) == PBM_TYPE && + (cmdline.padType == PAD_WHITE || cmdline.padType == PAD_BLACK)) + padPbm(ifP, cols, rows, format, + newcols, lpad, rpad, tpad, bpad, cmdline.padType, + stdout); + else + padGeneral(ifP, cols, rows, maxval, format, + newcols, lpad, rpad, tpad, bpad, cmdline.padType, + cmdline.color, cmdline.promote, stdout); + break; + } } pm_close(ifP); return 0; } + + + diff --git a/editor/pnmremap.c b/editor/pnmremap.c index e5b59d04..38e203f7 100644 --- a/editor/pnmremap.c +++ b/editor/pnmremap.c @@ -468,7 +468,7 @@ fserr_init(struct pam * const pamP, -----------------------------------------------------------------------------*/ unsigned int plane; - unsigned int const fserrSize = pamP->width + 2; + unsigned int const fserrSz = pamP->width + 2; fserrP->width = pamP->width; @@ -482,20 +482,20 @@ fserr_init(struct pam * const pamP, "for depth %u", pamP->depth); for (plane = 0; plane < pamP->depth; ++plane) { - MALLOCARRAY(fserrP->thiserr[plane], fserrSize); + MALLOCARRAY(fserrP->thiserr[plane], fserrSz); if (fserrP->thiserr[plane] == NULL) pm_error("Out of memory allocating Floyd-Steinberg structures " - "for Plane %u, size %u", plane, fserrSize); - MALLOCARRAY(fserrP->nexterr[plane], fserrSize); + "for Plane %u, size %u", plane, fserrSz); + MALLOCARRAY(fserrP->nexterr[plane], fserrSz); if (fserrP->nexterr[plane] == NULL) pm_error("Out of memory allocating Floyd-Steinberg structures " - "for Plane %u, size %u", plane, fserrSize); + "for Plane %u, size %u", plane, fserrSz); } if (random.init != RANDOM_NONE) - randomizeError(fserrP->thiserr, fserrSize, pamP->depth, random); + randomizeError(fserrP->thiserr, fserrSz, pamP->depth, random); else - zeroError(fserrP->thiserr, fserrSize, pamP->depth); + zeroError(fserrP->thiserr, fserrSz, pamP->depth); fserr_setForward(fserrP); } @@ -1014,7 +1014,7 @@ static void copyRaster(struct pam * const inpamP, struct pam * const outpamP, tupletable const colormap, - unsigned int const colormapSize, + unsigned int const colormapSz, bool const floyd, struct Random const random, tuple const defaultColor, @@ -1049,7 +1049,7 @@ copyRaster(struct pam * const inpamP, usehash = TRUE; - createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP); + createColormapFinder(outpamP, colormap, colormapSz, &colorFinderP); if (floyd) fserr_init(inpamP, &fserr, random); @@ -1082,13 +1082,13 @@ static void remap(FILE * const ifP, const struct pam * const outpamCommonP, tupletable const colormap, - unsigned int const colormapSize, + unsigned int const colormapSz, bool const floyd, struct Random const random, tuple const defaultColor, bool const verbose) { /*---------------------------------------------------------------------------- - Remap the pixels from the raster on *ifP to the 'colormapSize' colors in + Remap the pixels from the raster on *ifP to the 'colormapSz' colors in 'colormap'. Where the input pixel's color is in the map, just use that for the output. @@ -1129,7 +1129,7 @@ remap(FILE * const ifP, */ pnm_setminallocationdepth(&inpam, outpam.depth); - copyRaster(&inpam, &outpam, colormap, colormapSize, floyd, + copyRaster(&inpam, &outpam, colormap, colormapSz, floyd, random, defaultColor, &missingCount); if (verbose) @@ -1142,15 +1142,15 @@ remap(FILE * const ifP, static void -processMapFile(const char * const mapFileName, +processMapFile(const char * const mapFileNm, struct pam * const outpamCommonP, tupletable * const colormapP, - unsigned int * const colormapSizeP, + unsigned int * const colormapSzP, tuple * const firstColorP) { /*---------------------------------------------------------------------------- - Read a color map from the file named 'mapFileName'. It's a map that + Read a color map from the file named 'mapFileNm'. It's a map that associates each color in that file with a unique whole number. Return the - map as *colormapP, with the number of entries in it as *colormapSizeP. + map as *colormapP, with the number of entries in it as *colormapSzP. Also determine the first color (top left) in the map file and return that as *firstColorP. @@ -1160,11 +1160,11 @@ processMapFile(const char * const mapFileName, tuple ** maptuples; tuple firstColor; - mapfile = pm_openr(mapFileName); + mapfile = pm_openr(mapFileNm); maptuples = pnm_readpam(mapfile, &mappam, PAM_STRUCT_SIZE(tuple_type)); pm_close(mapfile); - computeColorMapFromMap(&mappam, maptuples, colormapP, colormapSizeP); + computeColorMapFromMap(&mappam, maptuples, colormapP, colormapSzP); firstColor = pnm_allocpamtuple(&mappam); pnm_assigntuple(&mappam, firstColor, maptuples[0][0]); @@ -1180,15 +1180,15 @@ processMapFile(const char * const mapFileName, static void getSpecifiedMissingColor(struct pam * const pamP, - const char * const colorName, + const char * const colorNm, tuple * const specColorP) { tuple specColor; specColor = pnm_allocpamtuple(pamP); - if (colorName) { - pixel const color = ppm_parsecolor(colorName, pamP->maxval); + if (colorNm) { + pixel const color = ppm_parsecolor(colorNm, pamP->maxval); if (pamP->depth == 3) { specColor[PAM_RED_PLANE] = PPM_GETR(color); specColor[PAM_GRN_PLANE] = PPM_GETG(color); @@ -1218,7 +1218,7 @@ main(int argc, const char * argv[] ) { across all output images. */ tupletable colormap; - unsigned int colormapSize; + unsigned int colormapSz; tuple specColor; /* A tuple of the color the user specified to use for input colors that are not in the colormap. Arbitrary tuple if he didn't @@ -1239,7 +1239,7 @@ main(int argc, const char * argv[] ) { ifP = pm_openr(cmdline.inputFilespec); processMapFile(cmdline.mapFilespec, &outpamCommon, - &colormap, &colormapSize, &firstColor); + &colormap, &colormapSz, &firstColor); getSpecifiedMissingColor(&outpamCommon, cmdline.missingcolor, &specColor); @@ -1255,7 +1255,7 @@ main(int argc, const char * argv[] ) { break; } - remap(ifP, &outpamCommon, colormap, colormapSize, + remap(ifP, &outpamCommon, colormap, colormapSz, cmdline.floyd, cmdline.random, defaultColor, cmdline.verbose); diff --git a/editor/pnmscalefixed.c b/editor/pnmscalefixed.c index 7d33a9c2..1a89a2e5 100644 --- a/editor/pnmscalefixed.c +++ b/editor/pnmscalefixed.c @@ -19,6 +19,7 @@ ** */ +#include <limits.h> #include <math.h> #include "pm_c_util.h" @@ -215,7 +216,7 @@ compute_output_dimensions(const struct cmdline_info cmdline, int * newrowsP, int * newcolsP) { if (cmdline.pixels) { - if (rows * cols <= cmdline.pixels) { + if (rows <= cmdline.pixels / cols) { *newrowsP = rows; *newcolsP = cols; } else { @@ -446,6 +447,10 @@ main(int argc, char **argv ) { unfilled. We can address that by stretching, whereas the other case would require throwing away some of the input. */ + if (newcols > INT_MAX / SCALE) + pm_error("New image width (%d) is uncomputably large", newcols); + if (newrows > INT_MAX / SCALE) + pm_error("New image height (%d) is uncomputably large", newrows); sxscale = SCALE * newcols / cols; syscale = SCALE * newrows / rows; diff --git a/editor/ppmdither.c b/editor/ppmdither.c index df94cf34..2619c8b7 100644 --- a/editor/ppmdither.c +++ b/editor/ppmdither.c @@ -1,5 +1,5 @@ /*============================================================================= - pamdither + ppmdither =============================================================================== By Bryan Henderson, July 2006. @@ -23,7 +23,7 @@ #define MAX_DITH_POWER (((unsigned)sizeof(unsigned int)*8 - 1) / 2) -struct colorResolution { +struct ColorResolution { unsigned int c[3]; /* comp[PAM_RED_PLANE] is number of distinct red levels, etc. */ }; @@ -38,7 +38,7 @@ struct CmdlineInfo { */ const char * inputFileName; /* File name of input file */ unsigned int dim; - struct colorResolution colorRes; + struct ColorResolution colorRes; unsigned int verbose; }; @@ -139,7 +139,7 @@ typedef struct { 0-3, and blue value of 0-1 to a tuple with maxval 255. So you can ask it to scale (1,1,1) and it responds with (85, 85, 255). -----------------------------------------------------------------------------*/ - struct colorResolution colorRes; + struct ColorResolution colorRes; /* Number of values of each color component possible, i.e. maxval plus 1 */ @@ -148,7 +148,7 @@ typedef struct { certain function (see scaler_scale()) of the input red, green, and blue values. */ -} scaler; +} Scaler; @@ -186,10 +186,10 @@ allocScalerMap(unsigned int const size) { static void scaler_create(sample const outputMaxval, - struct colorResolution const colorRes, - scaler ** const scalerPP) { + struct ColorResolution const colorRes, + Scaler ** const scalerPP) { - scaler * scalerP; + Scaler * scalerP; unsigned int mapSize; if (UINT_MAX / colorRes.c[RED] / colorRes.c[GRN] / colorRes.c[BLU] < 1) @@ -240,7 +240,7 @@ scaler_create(sample const outputMaxval, static void -scaler_destroy(scaler * const scalerP) { +scaler_destroy(Scaler * const scalerP) { free(scalerP->out); @@ -250,7 +250,7 @@ scaler_destroy(scaler * const scalerP) { static tuple -scaler_scale(const scaler * const scalerP, +scaler_scale(const Scaler * const scalerP, unsigned int const red, unsigned int const grn, unsigned int const blu) { @@ -336,45 +336,37 @@ dithValue(unsigned int const yArg, static unsigned int ** -dithMatrix(unsigned int const dithPower) { +newDithMatrix(unsigned int const dithPower) { /*---------------------------------------------------------------------------- Create the dithering matrix for dimension 'dithDim'. Return it in newly malloc'ed storage. - Note that we assume 'dithPower' is small enough that the 'dithMatSize' - computed within fits in an int. Otherwise, results are undefined. + Note that we assume 'dithPower' is not greater than the number of bits in + an unsigned int. -----------------------------------------------------------------------------*/ unsigned int const dithDim = 1 << dithPower; unsigned int ** dithMat; + unsigned int y; assert(dithPower < sizeof(unsigned int) * 8); - { - unsigned int const dithMatSize = - (dithDim * sizeof(*dithMat)) + /* pointers */ - (dithDim * dithDim * sizeof(**dithMat)); /* data */ - - dithMat = malloc(dithMatSize); - - if (dithMat == NULL) - pm_error("Out of memory. " - "Cannot allocate %u bytes for dithering matrix.", - dithMatSize); - } - { - unsigned int * const rowStorage = (unsigned int *)&dithMat[dithDim]; - unsigned int y; - for (y = 0; y < dithDim; ++y) - dithMat[y] = &rowStorage[y * dithDim]; - } - { - unsigned int y; + MALLOCARRAY(dithMat, dithDim); + if (!dithMat) + pm_error("Cannot allocate %u-row dithering matrix index", dithDim); + else { for (y = 0; y < dithDim; ++y) { - unsigned int x; - for (x = 0; x < dithDim; ++x) - dithMat[y][x] = dithValue(y, x, dithPower); + MALLOCARRAY(dithMat[y], dithDim); + if (!dithMat[y]) + pm_error("Failed to allocate %uth row of " + "%ux%u dithering matrix", y, dithDim, dithDim); + else { + unsigned int x; + + for (x = 0; x < dithDim; ++x) + dithMat[y][x] = dithValue(y, x, dithPower); + } } } return dithMat; @@ -383,9 +375,25 @@ dithMatrix(unsigned int const dithPower) { static void +freeDithMatrix(unsigned int ** const dithMat, + unsigned int const dithPower) { + + unsigned int const dithDim = 1 << dithPower; + + unsigned int y; + + for (y = 0; y < dithDim; ++y) + free(dithMat[y]); + + free(dithMat); +} + + + +static void validateNoDitherOverflow(unsigned int const ditherMatrixArea, struct pam * const inpamP, - struct colorResolution const colorRes) { + struct ColorResolution const colorRes) { /*---------------------------------------------------------------------------- Validate that we'll be able to do the dithering calculations based on the parameters above without busting out of an integer. @@ -409,10 +417,10 @@ validateNoDitherOverflow(unsigned int const ditherMatrixArea, static void ditherRow(struct pam * const inpamP, const tuple * const inrow, - const scaler * const scalerP, + const Scaler * const scalerP, unsigned int ** const ditherMatrix, unsigned int const ditherMatrixArea, - struct colorResolution const colorRes, + struct ColorResolution const colorRes, unsigned int const row, unsigned int const modMask, struct pam * const outpamP, @@ -447,9 +455,9 @@ ditherRow(struct pam * const inpamP, static void ditherImage(struct pam * const inpamP, - const scaler * const scalerP, + const Scaler * const scalerP, unsigned int const dithPower, - struct colorResolution const colorRes, + struct ColorResolution const colorRes, struct pam * const outpamP, tuple *** const outTuplesP) { @@ -460,7 +468,7 @@ ditherImage(struct pam * const inpamP, /* And this into N to compute N % dithDim cheaply, since we know (though the compiler doesn't) that dithDim is a power of 2 */ - unsigned int ** const ditherMatrix = dithMatrix(dithPower); + unsigned int ** const ditherMatrix = newDithMatrix(dithPower); tuple * inrow; tuple ** outTuples; @@ -490,7 +498,7 @@ ditherImage(struct pam * const inpamP, colorRes, row, modMask, outpamP, outTuples[row]); } - free(ditherMatrix); + freeDithMatrix(ditherMatrix, dithPower); pnm_freepamrow(inrow); *outTuplesP = outTuples; } @@ -504,7 +512,7 @@ main(int argc, struct CmdlineInfo cmdline; FILE * ifP; tuple ** outTuples; /* Output image */ - scaler * scalerP; + Scaler * scalerP; struct pam inpam; struct pam outpam; diff --git a/editor/specialty/pamdeinterlace.c b/editor/specialty/pamdeinterlace.c index 666daa16..a21484a9 100644 --- a/editor/specialty/pamdeinterlace.c +++ b/editor/specialty/pamdeinterlace.c @@ -8,6 +8,8 @@ Contributed to the public domain. ******************************************************************************/ +#include <stdbool.h> + #include "pm_c_util.h" #include "pam.h" #include "shhopt.h" @@ -15,7 +17,7 @@ enum evenodd {EVEN, ODD}; -struct cmdlineInfo { +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ @@ -24,9 +26,10 @@ struct cmdlineInfo { }; + static void -parseCommandLine(int argc, char ** argv, - struct cmdlineInfo *cmdlineP) { +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. @@ -44,10 +47,10 @@ parseCommandLine(int argc, char ** argv, OPTENT3(0, "takeodd", OPT_FLAG, NULL, &takeodd, 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 */ + 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, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ free(option_def); @@ -65,26 +68,24 @@ parseCommandLine(int argc, char ** argv, else if (argc-1 == 1) cmdlineP->inputFilespec = argv[1]; else - pm_error("You specified too many arguments (%d). The only " - "argument is the optional input file specification.", + pm_error("You specified too many arguments (%u). The only " + "possible argument is the optional input file specification.", argc-1); } - - int -main(int argc, char *argv[]) { +main(int argc, const char ** argv) { FILE * ifP; tuple * tuplerow; /* Row from input image */ unsigned int row; - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; struct pam inpam; struct pam outpam; - pnm_init( &argc, argv ); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); diff --git a/editor/specialty/pamoil.c b/editor/specialty/pamoil.c index 2618ac12..d7b76e85 100644 --- a/editor/specialty/pamoil.c +++ b/editor/specialty/pamoil.c @@ -1,4 +1,4 @@ -/* pgmoil.c - read a portable pixmap and turn into an oil painting +/* pgmoil.c - read a PPM image and turn into an oil painting ** ** Copyright (C) 1990 by Wilson Bent (whb@hoh-2.att.com) ** Shamelessly butchered into a color version by Chris Sheppard @@ -12,60 +12,153 @@ ** implied warranty. */ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> -#include "pam.h" + #include "mallocvar.h" +#include "shhopt.h" +#include "pam.h" + + + +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileNm; + unsigned int n; +}; + + + +static void +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct3 opt; /* set by OPTENT3 */ + optEntry * option_def; + unsigned int option_def_index; + + unsigned int nSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "n", OPT_UINT, &cmdlineP->n, &nSpec, 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. */ + + free(option_def); + + if (!nSpec) + cmdlineP->n = 3; + + if (argc-1 < 1) + cmdlineP->inputFileNm = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileNm = argv[1]; + else + pm_error("You specified too many arguments (%u). The only " + "possible argument is the optional input file specification.", + argc-1); +} + + + +static void +computeRowHist(struct pam const inpam, + tuple ** const tuples, + unsigned int const smearFactor, + unsigned int const plane, + unsigned int const row, + unsigned int const col, + sample * const hist) { +/*---------------------------------------------------------------------------- + Compute hist[] - frequencies, in the neighborhood of row 'row', column + 'col', in plane 'plane', of each sample value +-----------------------------------------------------------------------------*/ + sample i; + int drow; + + for (i = 0; i <= inpam.maxval; ++i) + hist[i] = 0; + + for (drow = row - smearFactor; drow <= row + smearFactor; ++drow) { + if (drow >= 0 && drow < inpam.height) { + int dcol; + + for (dcol = col - smearFactor; + dcol <= col + smearFactor; + ++dcol) { + if (dcol >= 0 && dcol < inpam.width) + ++hist[tuples[drow][dcol][plane]]; + } + } + } +} + + + +static sample +modalValue(sample * const hist, + sample const maxval) { +/*---------------------------------------------------------------------------- + The sample value that occurs most often according to histogram hist[]. +-----------------------------------------------------------------------------*/ + sample modalval; + unsigned int maxfreq; + sample sampleval; + + for (sampleval = 0, maxfreq = 0, modalval = 0; + sampleval <= maxval; + ++sampleval) { + + if (hist[sampleval] > maxfreq) { + maxfreq = hist[sampleval]; + modalval = sampleval; + } + } + return modalval; +} + + static void -convertRow(struct pam const inpam, tuple ** const tuples, - tuple * const tuplerow, int const row, int const smearFactor, - int * const hist) { +convertRow(struct pam const inpam, + tuple ** const tuples, + tuple * const tuplerow, + unsigned int const row, + unsigned int const smearFactor, + sample * const hist) { +/*---------------------------------------------------------------------------- + 'hist' is a working buffer inpam.width wide. +-----------------------------------------------------------------------------*/ + unsigned int plane; + + for (plane = 0; plane < inpam.depth; plane++) { + unsigned int col; - int sample; - for (sample = 0; sample < inpam.depth; sample++) { - int col; for (col = 0; col < inpam.width; ++col) { - int i; - int drow; - int modalval; + sample modalval; /* The sample value that occurs most often in the neighborhood - of the pixel being examined + of column 'col' of row 'row', in plane 'plane'. */ - /* Compute hist[] - frequencies, in the neighborhood, of each - sample value - */ - for (i = 0; i <= inpam.maxval; ++i) hist[i] = 0; - - for (drow = row - smearFactor; drow <= row + smearFactor; ++drow) { - if (drow >= 0 && drow < inpam.height) { - int dcol; - for (dcol = col - smearFactor; - dcol <= col + smearFactor; - ++dcol) { - if ( dcol >= 0 && dcol < inpam.width ) - ++hist[tuples[drow][dcol][sample]]; - } - } - } - { - /* Compute modalval */ - int sampleval; - int maxfreq; - - maxfreq = 0; - modalval = 0; - - for (sampleval = 0; sampleval <= inpam.maxval; ++sampleval) { - if (hist[sampleval] > maxfreq) { - maxfreq = hist[sampleval]; - modalval = sampleval; - } - } - } - tuplerow[col][sample] = modalval; + computeRowHist(inpam, tuples, smearFactor, plane, row, col, hist); + + modalval = modalValue(hist, inpam.maxval); + + tuplerow[col][plane] = modalval; } } } @@ -73,44 +166,24 @@ convertRow(struct pam const inpam, tuple ** const tuples, int -main(int argc, char *argv[] ) { +main(int argc, const char ** argv) { + struct pam inpam, outpam; - FILE* ifp; - tuple ** tuples; - tuple * tuplerow; - int * hist; + FILE * ifP; + tuple ** tuples; /* malloc'ed */ + tuple * tuplerow; /* malloc'ed */ + sample * hist; /* malloc'ed */ /* A buffer for the convertRow subroutine to use */ - int argn; int row; - int smearFactor; - const char* const usage = "[-n <n>] [ppmfile]"; - - ppm_init( &argc, argv ); - - argn = 1; - smearFactor = 3; /* DEFAULT VALUE */ - - /* Check for options. */ - if ( argn < argc && argv[argn][0] == '-' ) { - if ( argv[argn][1] == 'n' ) { - ++argn; - if ( argn == argc || sscanf(argv[argn], "%d", &smearFactor) != 1 ) - pm_usage( usage ); - } else - pm_usage( usage ); - ++argn; - } - if ( argn < argc ) { - ifp = pm_openr( argv[argn] ); - ++argn; - } else - ifp = stdin; + struct CmdlineInfo cmdline; + + pm_proginit(&argc, argv); - if ( argn != argc ) - pm_usage( usage ); + parseCommandLine(argc, argv, &cmdline); - tuples = pnm_readpam(ifp, &inpam, PAM_STRUCT_SIZE(tuple_type)); - pm_close(ifp); + ifP = pm_openr(cmdline.inputFileNm); + + tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); MALLOCARRAY(hist, inpam.maxval + 1); if (hist == NULL) @@ -123,7 +196,7 @@ main(int argc, char *argv[] ) { tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { - convertRow(inpam, tuples, tuplerow, row, smearFactor, hist); + convertRow(inpam, tuples, tuplerow, row, cmdline.n, hist); pnm_writepamrow(&outpam, tuplerow); } @@ -131,7 +204,9 @@ main(int argc, char *argv[] ) { free(hist); pnm_freepamarray(tuples, &inpam); + pm_close(ifP); pm_close(stdout); - exit(0); + return 0; } + diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c index dcb183ef..0214ee8b 100644 --- a/editor/specialty/pnmindex.c +++ b/editor/specialty/pnmindex.c @@ -2,7 +2,7 @@ pnmindex ============================================================================== - build a visual index of a bunch of PNM images + Build a visual index of a bunch of PNM images. This used to be a C shell program, and then a BASH program. Neither were portable enough, and the program is too complex for either of @@ -32,6 +32,7 @@ #include "shhopt.h" #include "mallocvar.h" #include "nstring.h" +#include "pm_system.h" #include "pnm.h" struct CmdlineInfo { @@ -53,57 +54,6 @@ static bool verbose; -static void PM_GNU_PRINTF_ATTR(1,2) -systemf(const char * const fmt, - ...) { - - va_list varargs; - - size_t dryRunLen; - - va_start(varargs, fmt); - - dryRunLen = vsnprintf(NULL, 0, fmt, varargs); - - va_end(varargs); - - if (dryRunLen + 1 < dryRunLen) - /* arithmetic overflow */ - pm_error("Command way too long"); - else { - size_t const allocSize = dryRunLen + 1; - char * shellCommand; - shellCommand = malloc(allocSize); - if (shellCommand == NULL) - pm_error("Can't get storage for %u-character command", - (unsigned)allocSize); - else { - va_list varargs; - size_t realLen; - int rc; - - va_start(varargs, fmt); - - realLen = vsnprintf(shellCommand, allocSize, fmt, varargs); - - assert(realLen == dryRunLen); - va_end(varargs); - - if (verbose) - pm_message("shell cmd: %s", shellCommand); - - rc = system(shellCommand); - if (rc != 0) - pm_error("shell command '%s' failed. rc %d", - shellCommand, rc); - - pm_strfree(shellCommand); - } - } -} - - - static const char * shellQuote(const char * const arg) { /*---------------------------------------------------------------------------- @@ -170,7 +120,7 @@ shellQuote(const char * const arg) { static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * const cmdlineP) { unsigned int option_def_index; @@ -206,19 +156,28 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; - pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdline_p and others. */ if (quant && cmdlineP->noquant) - pm_error("You can't specify both -quant and -noquat"); + pm_error("You can't specify both -quant and -noquant"); - if (!colorsSpec) + if (colorsSpec) { + if (cmdlineP->colors == 0) + pm_error("-colors value must be positive"); + } else cmdlineP->colors = 256; - if (!sizeSpec) + if (sizeSpec) { + if (cmdlineP->size == 0) + pm_error("-size value must be positive"); + } else cmdlineP->size = 100; - if (!acrossSpec) + if (acrossSpec) { + if (cmdlineP->across == 0) + pm_error("-across value must be positive"); + } else cmdlineP->across = 6; if (!titleSpec) @@ -340,85 +299,118 @@ static void makeTitle(const char * const title, unsigned int const rowNumber, bool const blackBackground, - const char * const tempDir) { + const char * const dirNm) { +/*---------------------------------------------------------------------------- + Create a PBM file containing the text 'title'. + + If 'blackBackground' is true, make it white on black; otherwise, black + on white. + Name the file like a thumbnail row file for row number 'rowNumber', + in directory named 'dirNm'. +-----------------------------------------------------------------------------*/ const char * const invertStage = blackBackground ? "| pnminvert " : ""; - const char * const titleToken = shellQuote(title); const char * fileName; + FILE * outFP; + struct bufferDesc titleBuf; + const char * shellCommand; + int termStatus; - fileName = rowFileName(tempDir, rowNumber); - - /* This quoting is not adequate. We really should do this without - a shell at all. - */ - systemf("pbmtext %s %s > %s", - titleToken, invertStage, fileName); + titleBuf.size = strlen(title); + titleBuf.buffer = (unsigned char *)title; + titleBuf.bytesTransferredP = NULL; + fileName = rowFileName(dirNm, rowNumber); + outFP = pm_openw(fileName); pm_strfree(fileName); - pm_strfree(titleToken); + + pm_asprintf(&shellCommand, "pbmtext %s", invertStage); + + pm_system2(&pm_feed_from_memory, &titleBuf, + &pm_accept_to_filestream, outFP, + shellCommand, &termStatus); + + pm_strfree(shellCommand); + + if (termStatus != 0) + pm_error("Failed to generate title image"); + + pm_close(outFP); } static void -copyImage(const char * const inputFileName, - const char * const outputFileName) { +copyImage(const char * const inputFileNm, + const char * const outputFileNm) { - const char * const inputFileNmToken = shellQuote(inputFileName); + int termStatus; - systemf("cat %s > %s", inputFileNmToken, outputFileName); + pm_system2_lp("cat", + &pm_feed_from_file, (void*)inputFileNm, + &pm_accept_to_file, (void*)outputFileNm, + &termStatus, + "cat", NULL); - pm_strfree(inputFileNmToken); + if (termStatus != 0) + pm_error("'cat' to copy image '%s' to '%s' failed, " + "termination status = %d", + inputFileNm, outputFileNm, termStatus); } static void -copyScaleQuantImage(const char * const inputFileName, - const char * const outputFileName, +copyScaleQuantImage(const char * const inputFileNm, + const char * const outputFileNm, int const format, unsigned int const size, unsigned int const quant, - unsigned int const colors) { - - const char * const inputFileNmToken = shellQuote(inputFileName); + unsigned int const colorCt) { const char * scaleCommand; + int termStatus; switch (PNM_FORMAT_TYPE(format)) { case PBM_TYPE: pm_asprintf(&scaleCommand, - "pamscale -quiet -xysize %u %u %s " - "| pgmtopbm > %s", - size, size, inputFileNmToken, outputFileName); + "pamscale -quiet -xysize %u %u " + "| pamditherbw", + size, size); break; case PGM_TYPE: pm_asprintf(&scaleCommand, - "pamscale -quiet -xysize %u %u %s >%s", - size, size, inputFileNmToken, outputFileName); + "pamscale -quiet -xysize %u %u", + size, size); break; case PPM_TYPE: if (quant) pm_asprintf(&scaleCommand, - "pamscale -quiet -xysize %u %u %s " - "| pnmquant -quiet %u > %s", - size, size, inputFileNmToken, colors, outputFileName); + "pamscale -quiet -xysize %u %u " + "| pnmquant -quiet %u ", + size, size, colorCt); else pm_asprintf(&scaleCommand, - "pamscale -quiet -xysize %u %u %s >%s", - size, size, inputFileNmToken, outputFileName); + "pamscale -quiet -xysize %u %u ", + size, size); break; default: pm_error("Unrecognized Netpbm format: %d", format); } - systemf("%s", scaleCommand); + pm_system2(pm_feed_from_file, (void*)inputFileNm, + pm_accept_to_file, (void*)outputFileNm, + scaleCommand, + &termStatus); + + if (termStatus != 0) + pm_message("Shell command '%s' failed. Termination status=%d", + scaleCommand, termStatus); pm_strfree(scaleCommand); - pm_strfree(inputFileNmToken); } @@ -485,10 +477,10 @@ thumbnailFileList(const char * const dirName, static void -makeImageFile(const char * const thumbnailFileName, - const char * const inputFileName, +makeImageFile(const char * const thumbnailFileNm, + const char * const inputFileNm, bool const blackBackground, - const char * const outputFileName) { + const char * const outputFileNm) { /*---------------------------------------------------------------------------- Create one thumbnail image. It consists of the image in the file named 'thumbnailFileName' with text of that name appended to the bottom. @@ -501,15 +493,34 @@ makeImageFile(const char * const thumbnailFileName, -----------------------------------------------------------------------------*/ const char * const blackWhiteOpt = blackBackground ? "-black" : "-white"; const char * const invertStage = blackBackground ? "| pnminvert " : ""; - const char * inputFileNmToken = shellQuote(inputFileName); - - systemf("pbmtext %s %s" - "| pamcat %s -topbottom %s - " - "> %s", - inputFileNmToken, invertStage, blackWhiteOpt, - thumbnailFileName, outputFileName); - - pm_strfree(inputFileNmToken); + const char * const thumbnailFileNmToken = shellQuote(thumbnailFileNm); + + struct bufferDesc fileNmBuf; + const char * shellCommand; + int termStatus; + + fileNmBuf.size = strlen(inputFileNm); + fileNmBuf.buffer = (unsigned char *)inputFileNm; + fileNmBuf.bytesTransferredP = NULL; + + pm_asprintf(&shellCommand, + "pbmtext " + "%s" + "| pamcat %s -topbottom %s - ", + invertStage, blackWhiteOpt, thumbnailFileNmToken); + + pm_system2(&pm_feed_from_memory, &fileNmBuf, + &pm_accept_to_file, (void*)outputFileNm, + shellCommand, + &termStatus); + + if (termStatus != 0) + pm_error("Shell command '%s' to add file name to thumbnail image " + "of file '%s' failed, termination Status = %d", + shellCommand, inputFileNm, termStatus); + + pm_strfree(thumbnailFileNmToken); + pm_strfree(shellCommand); } @@ -519,17 +530,18 @@ makeThumbnail(const char * const inputFileName, unsigned int const size, bool const black, bool const quant, - unsigned int const colors, + unsigned int const colorCt, const char * const tempDir, unsigned int const row, unsigned int const col, int * const formatP) { + const char * const fileName = thumbnailFileName(tempDir, row, col); + FILE * ifP; int imageCols, imageRows, format; xelval maxval; const char * tmpfile; - const char * fileName; ifP = pm_openr(inputFileName); pnm_readpnminit(ifP, &imageCols, &imageRows, &maxval, &format); @@ -541,9 +553,7 @@ makeThumbnail(const char * const inputFileName, copyImage(inputFileName, tmpfile); else copyScaleQuantImage(inputFileName, tmpfile, format, - size, quant, colors); - - fileName = thumbnailFileName(tempDir, row, col); + size, quant, colorCt); makeImageFile(tmpfile, inputFileName, black, fileName); @@ -594,40 +604,66 @@ unlinkRowFiles(const char * const dirName, static void combineIntoRowAndDelete(unsigned int const row, - unsigned int const cols, + unsigned int const colCt, int const maxFormatType, bool const blackBackground, bool const quant, - unsigned int const colors, + unsigned int const colorCt, const char * const tempDir) { +/*---------------------------------------------------------------------------- + Combine the 'colCt' thumbnails for row 'row' into a PNM file in directory + 'tempDir'. + + Each thumbnail is from a specially named file in 'tempDir' whose name + indicates its position in the row, and the output is also specially named + with a name indicating it is row 'row'. + + Where the thumnails are different heights, pad with black if + 'blackBackground' is true; white otherwise. + If 'quant', color-quantize the result to have no more than 'colorCt' + colors, choosing the colors that best represent all the pixels in the + result. + + 'maxFormatType' is the most expressive format of all the thumbnail files; + results are undefined if it is not. +-----------------------------------------------------------------------------*/ const char * const blackWhiteOpt = blackBackground ? "-black" : "-white"; + const char * const fileNm = rowFileName(tempDir, row); - const char * fileName; const char * quantStage; const char * fileList; + const char * shellCommand; + int termStatus; - fileName = rowFileName(tempDir, row); - - unlink(fileName); + unlink(fileNm); if (maxFormatType == PPM_TYPE && quant) - pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colors); + pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colorCt); else quantStage = strdup(""); - fileList = thumbnailFileList(tempDir, row, cols); + fileList = thumbnailFileList(tempDir, row, colCt); + + pm_asprintf(&shellCommand, "pamcat %s -leftright -jbottom %s " + "%s", + blackWhiteOpt, fileList, quantStage); - systemf("pamcat %s -leftright -jbottom %s " - "%s" - ">%s", - blackWhiteOpt, fileList, quantStage, fileName); + pm_system2(&pm_feed_null, NULL, + &pm_accept_to_file, (void*)fileNm, + shellCommand, + &termStatus); + + if (termStatus != 0) + pm_error("Shell command '%s' to create row of thumbnails failed. " + "Termination status = %d", shellCommand, termStatus); pm_strfree(fileList); pm_strfree(quantStage); - pm_strfree(fileName); + pm_strfree(fileNm); + pm_strfree(shellCommand); - unlinkThumbnailFiles(tempDir, row, cols); + unlinkThumbnailFiles(tempDir, row, colCt); } @@ -666,38 +702,70 @@ rowFileList(const char * const dirName, static void -writeRowsAndDelete(unsigned int const rows, +writeRowsAndDelete(unsigned int const rowCt, int const maxFormatType, bool const blackBackground, bool const quant, - unsigned int const colors, + unsigned int const colorCt, const char * const tempDir) { +/*---------------------------------------------------------------------------- + Write the PNM image containing the 'rowCt' rows of thumbnails to Standard + Output. Take each row of thumbnails from a specially named PNM file in + directory 'tempDir', and unlink it from that directory. + + Where the rows are different widths, pad with black if 'blackBackground' + is true; white otherwise. + + If 'quant', color-quantize the result to have no more than 'colorCt' + colors, choosing the colors that best represent all the pixels in the + result. + 'maxFormatType' is the most expressive format of all the row files; results + are undefined if it is not. +-----------------------------------------------------------------------------*/ const char * const blackWhiteOpt = blackBackground ? "-black" : "-white"; + const char * const plainOpt = pm_plain_output ? "-plain" : ""; const char * quantStage; const char * fileList; + const char * shellCommand; + int termStatus; if (maxFormatType == PPM_TYPE && quant) - pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colors); + pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colorCt); else quantStage = strdup(""); - fileList = rowFileList(tempDir, rows); + fileList = rowFileList(tempDir, rowCt); + + pm_asprintf(&shellCommand, "pamcat %s %s -topbottom %s %s", + plainOpt, blackWhiteOpt, fileList, quantStage); + + /* Do pamcat/pnmquant command with no Standard Input and writing to + our Standard Output + */ + pm_system2(&pm_feed_null, NULL, + NULL, NULL, + shellCommand, + &termStatus); - systemf("pamcat %s -topbottom %s %s", - blackWhiteOpt, fileList, quantStage); + if (termStatus != 0) + pm_error("Shell command '%s' to assemble %u rows of thumbnails and " + "write them out failed; termination status = %d", + shellCommand, rowCt, termStatus); + pm_strfree(shellCommand); pm_strfree(fileList); pm_strfree(quantStage); - unlinkRowFiles(tempDir, rows); + unlinkRowFiles(tempDir, rowCt); } int -main(int argc, char *argv[]) { +main(int argc, const char ** argv) { + struct CmdlineInfo cmdline; const char * tempDir; int maxFormatType; @@ -705,7 +773,7 @@ main(int argc, char *argv[]) { unsigned int rowsDone; unsigned int i; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); @@ -713,14 +781,15 @@ main(int argc, char *argv[]) { makeTempDir(&tempDir); - maxFormatType = PBM_TYPE; - colsInRow = 0; - rowsDone = 0; + rowsDone = 0; /* initial value */ if (cmdline.title) makeTitle(cmdline.title, rowsDone++, cmdline.black, tempDir); - for (i = 0; i < cmdline.inputFileCount; ++i) { + for (i = 0, colsInRow = 0, maxFormatType = PBM_TYPE; + i < cmdline.inputFileCount; + ++i) { + const char * const inputFileName = cmdline.inputFileName[i]; int format; @@ -755,4 +824,3 @@ main(int argc, char *argv[]) { } - |