diff options
Diffstat (limited to 'other')
-rw-r--r-- | other/pamstack.c | 189 | ||||
-rw-r--r-- | other/pnmcolormap.c | 173 |
2 files changed, 253 insertions, 109 deletions
diff --git a/other/pamstack.c b/other/pamstack.c index 308852c8..75b66cb7 100644 --- a/other/pamstack.c +++ b/other/pamstack.c @@ -23,22 +23,30 @@ #define MAX_INPUTS 16 /* The most input PAMs we allow user to specify */ -struct cmdlineInfo { +enum MaxvalScaling { + /* How to scale maxvals if the inputs don't all have the same maxval */ + MAXVALSCALE_NONE, /* Don't scale -- fail program */ + MAXVALSCALE_FIRST, /* Scale everything to maxval of first input */ + MAXVALSCALE_LCM /* Scale everything to least common multiple */ +}; + +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char *tupletype; /* Tuple type for output PAM */ unsigned int nInput; /* The number of input PAMs. At least 1, at most 16. */ const char * inputFileName[MAX_INPUTS]; /* The PAM files to combine, in order. */ + const char * tupletype; + enum MaxvalScaling maxvalScaling; }; 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 strings we return are stored in the storage that was passed to us as the argv array. @@ -50,19 +58,21 @@ parseCommandLine(int argc, char ** argv, extern struct pam pam; /* Just so we can look at field sizes */ unsigned int option_def_index; - unsigned int tupletypeSpec; + unsigned int tupletypeSpec, firstmaxvalSpec, lcmmaxvalSpec; MALLOCARRAY_NOFAIL(option_def, 100); - + option_def_index = 0; /* incremented by OPTENTRY */ - OPTENT3(0, "tupletype", OPT_STRING, &cmdlineP->tupletype, + OPTENT3(0, "tupletype", OPT_STRING, &cmdlineP->tupletype, &tupletypeSpec, 0); + OPTENT3(0, "firstmaxval", OPT_FLAG, NULL, &firstmaxvalSpec, 0); + OPTENT3(0, "lcmmaxval", OPT_FLAG, NULL, &lcmmaxvalSpec, 0); opt.opt_table = option_def; 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. */ if (!tupletypeSpec) @@ -73,12 +83,25 @@ parseCommandLine(int argc, char ** argv, "%u characters allowed.", (unsigned)sizeof(pam.tuple_type)); + if (firstmaxvalSpec) { + if (lcmmaxvalSpec) + pm_error("Cannot specify both -lcmmaxval and -firstmaxval"); + else + cmdlineP->maxvalScaling = MAXVALSCALE_FIRST; + } else if (lcmmaxvalSpec) { + if (firstmaxvalSpec) + pm_error("Cannot specify both -lcmmaxval and -firstmaxval"); + else + cmdlineP->maxvalScaling = MAXVALSCALE_LCM; + } else + cmdlineP->maxvalScaling = MAXVALSCALE_NONE; + cmdlineP->nInput = 0; /* initial value */ - { + { unsigned int argn; bool stdinUsed; for (argn = 1, stdinUsed = false; argn < argc; ++argn) { - if (cmdlineP->nInput >= MAX_INPUTS) + if (cmdlineP->nInput >= MAX_INPUTS) pm_error("You may not specify more than %u input images.", MAX_INPUTS); cmdlineP->inputFileName[cmdlineP->nInput++] = argv[argn]; @@ -110,51 +133,71 @@ openAllStreams(unsigned int const nInput, static void -outputRaster(const struct pam inpam[], - unsigned int const nInput, - struct pam outpam) { +outputRaster(const struct pam * const inpam, /* array */ + unsigned int const nInput, + struct pam const outpam) { +/*---------------------------------------------------------------------------- + Write the raster of the output image according to 'outpam'. Compose it + from the 'nInput' input images described by 'inpam'. + + 'outpam' may indicate a different maxval from some or all of the input + images. +-----------------------------------------------------------------------------*/ + tuple * inrow; + tuple * outrow; - tuple *inrow; - tuple *outrow; - outrow = pnm_allocpamrow(&outpam); - inrow = pnm_allocpamrow(&outpam); + inrow = pnm_allocpamrow(&outpam); - { - int row; - - for (row = 0; row < outpam.height; row++) { + { + unsigned int row; + + for (row = 0; row < outpam.height; ++row) { unsigned int inputSeq; - int outplane; - outplane = 0; /* initial value */ - for (inputSeq = 0; inputSeq < nInput; ++inputSeq) { + unsigned int outPlane; + + for (inputSeq = 0, outPlane = 0; inputSeq < nInput; ++inputSeq) { struct pam thisInpam = inpam[inputSeq]; - int col; + unsigned int col; pnm_readpamrow(&thisInpam, inrow); - for (col = 0; col < outpam.width; col ++) { - int inplane; - for (inplane = 0; inplane < thisInpam.depth; ++inplane) - outrow[col][outplane+inplane] = inrow[col][inplane]; + pnm_scaletuplerow(&thisInpam, inrow, inrow, outpam.maxval); + + for (col = 0; col < outpam.width; ++col) { + unsigned int inPlane; + for (inPlane = 0; inPlane < thisInpam.depth; ++inPlane) { + outrow[col][outPlane+inPlane] = inrow[col][inPlane]; + } } - outplane += thisInpam.depth; + outPlane += thisInpam.depth; } pnm_writepamrow(&outpam, outrow); } } pnm_freepamrow(outrow); - pnm_freepamrow(inrow); + pnm_freepamrow(inrow); } static void -processOneImageInAllStreams(unsigned int const nInput, - FILE * const ifP[], - FILE * const ofP, - const char * const tupletype) { +processOneImageInAllStreams(unsigned int const nInput, + FILE * const ifP[], + FILE * const ofP, + const char * const tupletype, + enum MaxvalScaling const maxvalScaling) { +/*---------------------------------------------------------------------------- + Take one image from each of the 'nInput' open input streams ifP[] + and stack them into one output image on *ofP. + Take the images from the current positions of those streams and leave + the streams positioned after them. + + Make the output image have tuple type 'tupletype'. + + Scale input samples for output according to 'maxvalScaling'. +-----------------------------------------------------------------------------*/ struct pam inpam[MAX_INPUTS]; /* Input PAM images */ struct pam outpam; /* Output PAM image */ @@ -166,30 +209,61 @@ processOneImageInAllStreams(unsigned int const nInput, unsigned int outputDepth; outputDepth = 0; /* initial value */ - - for (inputSeq = 0; inputSeq < nInput; ++inputSeq) { + sample maxvalLcm; + /* Least common multiple of all maxvals or PNM_OVERALLMAXVAL if the + LCM is greater than that. + */ + bool allImagesSameMaxval; + /* The images all have the same maxval */ - pnm_readpaminit(ifP[inputSeq], &inpam[inputSeq], + for (inputSeq = 0, allImagesSameMaxval = true, maxvalLcm = 1; + inputSeq < nInput; + ++inputSeq) { + + pnm_readpaminit(ifP[inputSeq], &inpam[inputSeq], PAM_STRUCT_SIZE(tuple_type)); - if (inputSeq > 0) { - /* All images, including this one, must be compatible with the - first image. - */ - if (inpam[inputSeq].width != inpam[0].width) - pm_error("Image no. %u does not have the same width as " - "Image 0.", inputSeq); - if (inpam[inputSeq].height != inpam[0].height) - pm_error("Image no. %u does not have the same height as " - "Image 0.", inputSeq); - if (inpam[inputSeq].maxval != inpam[0].maxval) - pm_error("Image no. %u does not have the same maxval as " - "Image 0.", inputSeq); - } + /* All images, including this one, must have same dimensions as + the first image. + */ + if (inpam[inputSeq].width != inpam[0].width) + pm_error("Image no. %u does not have the same width as " + "Image 0.", inputSeq); + if (inpam[inputSeq].height != inpam[0].height) + pm_error("Image no. %u does not have the same height as " + "Image 0.", inputSeq); + + if (inpam[inputSeq].maxval != inpam[0].maxval) + allImagesSameMaxval = false; + + maxvalLcm = pm_lcm(maxvalLcm, inpam[inputSeq].maxval, 1, + PAM_OVERALL_MAXVAL); + outputDepth += inpam[inputSeq].depth; } outpam = inpam[0]; /* Initial value */ + + switch (maxvalScaling) { + case MAXVALSCALE_NONE: + if (!allImagesSameMaxval) + pm_message("Inputs do not all have same maxval. " + "Consider -firstmaxval or -lcmmaxval"); + outpam.maxval = inpam[0].maxval; + break; + case MAXVALSCALE_FIRST: + outpam.maxval = inpam[0].maxval; + if (!allImagesSameMaxval) + pm_message("Input maxvals vary; making output maxval %lu " + "per -firstmaxval", outpam.maxval); + break; + case MAXVALSCALE_LCM: + outpam.maxval = maxvalLcm; + if (!allImagesSameMaxval) + pm_message("Input maxvals vary; making output maxval %lu " + "per -lcmmaxval", outpam.maxval); + break; + } outpam.depth = outputDepth; outpam.file = ofP; outpam.format = PAM_FORMAT; @@ -226,13 +300,13 @@ nextImageAllStreams(unsigned int const nInput, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP[MAX_INPUTS]; bool eof; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); @@ -241,10 +315,13 @@ main(int argc, char *argv[]) { eof = FALSE; while (!eof) { processOneImageInAllStreams(cmdline.nInput, ifP, stdout, - cmdline.tupletype); + cmdline.tupletype, cmdline.maxvalScaling); nextImageAllStreams(cmdline.nInput, ifP, &eof); } return 0; } + + + diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c index fbe85d4e..15d664d7 100644 --- a/other/pnmcolormap.c +++ b/other/pnmcolormap.c @@ -42,6 +42,8 @@ struct Box { A box contains an extent of a color frequency table, i.e. the colors with some consecutive index values in the color frequency table. -----------------------------------------------------------------------------*/ + unsigned int serialNum; + /* Unique identifier of this box; sequence number of creation. */ unsigned int startIndex; /* First index in the extent */ unsigned int colorCt; @@ -138,14 +140,14 @@ parseCommandLine (int argc, const char ** argv, NULL, &splitcolorct, 0); OPTENT3(0, "splitspread", OPT_FLAG, NULL, &splitspread, 0); - OPTENT3(0, "sort", OPT_FLAG, NULL, - &cmdlineP->sort, 0 ); - OPTENT3(0, "square", OPT_FLAG, NULL, - &cmdlineP->square, 0 ); - OPTENT3(0, "verbose", OPT_FLAG, NULL, - &cmdlineP->verbose, 0 ); - OPTENT3(0, "debug", OPT_FLAG, NULL, - &cmdlineP->debug, 0 ); + OPTENT3(0, "sort", OPT_FLAG, NULL, + &cmdlineP->sort, 0); + OPTENT3(0, "square", OPT_FLAG, NULL, + &cmdlineP->square, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3(0, "debug", OPT_FLAG, NULL, + &cmdlineP->debug, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -220,28 +222,63 @@ parseCommandLine (int argc, const char ** argv, #ifndef LITERAL_FN_DEF_MATCH -static qsort_comparison_fn compareplane; +static qsort_comparison_fn compareColor; #endif -static unsigned int compareplanePlane; - /* This is a parameter to compareplane(). We use this global variable - so that compareplane() can be called by qsort(), to compare two - tuples. qsort() doesn't pass any arguments except the two tuples. - */ +static struct { +/*---------------------------------------------------------------------------- + This is a parameter to compareColor(). We use this global variable + so that compareColor() can be called by qsort(), to compare two + tuples. qsort() doesn't pass any arguments except the two tuples. +-----------------------------------------------------------------------------*/ + unsigned int comparePlane; + /* The number of the plane to compare between the tuples */ + unsigned int colorDepth; + /* Depth (number of planes) of the tuples */ +} compareColorParm; + static int -compareplane(const void * const arg1, +compareColor(const void * const arg1, const void * const arg2) { const struct tupleint * const * const comparandPP = arg1; const struct tupleint * const * const comparatorPP = arg2; - sample const comparandSample = (*comparandPP) ->tuple[compareplanePlane]; - sample const comparatorSample = (*comparatorPP)->tuple[compareplanePlane]; + sample const comparandSample = + (*comparandPP) ->tuple[compareColorParm.comparePlane]; + sample const comparatorSample = + (*comparatorPP)->tuple[compareColorParm.comparePlane]; - return - comparandSample < comparatorSample ? -1 : - comparandSample > comparatorSample ? 1 : - 0; + int retval; + + if (comparandSample < comparatorSample) + retval = -1; + else if (comparandSample > comparatorSample) + retval = +1; + else { + /* In the plane that matters, samples are equal, but we're going to + try to differentiate the colors anyway so as to make qsort put the + colors in a deterministic order so the boxes are deterministic. + */ + unsigned int plane; + int bestDiffSoFar; /* -1, 0, or 1, like our return value */ + for (plane = 0, bestDiffSoFar = 0; + plane < compareColorParm.colorDepth && bestDiffSoFar == 0; + ++plane) { + + sample const comparandSample = + (*comparandPP) ->tuple[compareColorParm.comparePlane]; + sample const comparatorSample = + (*comparatorPP)->tuple[compareColorParm.comparePlane]; + + if (comparandSample < comparatorSample) + bestDiffSoFar = -1; + else if (comparandSample > comparatorSample) + bestDiffSoFar = +1; + } + retval = bestDiffSoFar; + } + return retval; } @@ -259,7 +296,9 @@ sumcompare(const void * const arg1, return comparatorP->sum < comparandP->sum ? -1 : - comparatorP->sum > comparandP->sum ? 1 : + comparatorP->sum > comparandP->sum ? +1 : + comparatorP->serialNum < comparandP->serialNum ? -1 : + comparatorP->serialNum > comparandP->serialNum ? +1 : 0; } @@ -279,6 +318,8 @@ colcompare(const void * const arg1, return comparatorP->colorCt < comparandP->colorCt ? -1 : comparatorP->colorCt > comparandP->colorCt ? 1 : + comparatorP->serialNum < comparandP->serialNum ? -1 : + comparatorP->serialNum > comparandP->serialNum ? +1 : 0; } @@ -298,6 +339,8 @@ spreadcompare(const void * const arg1, return comparatorP->spread < comparandP->spread ? -1 : comparatorP->spread > comparandP->spread ? 1 : + comparatorP->serialNum < comparandP->serialNum ? -1 : + comparatorP->serialNum > comparandP->serialNum ? +1 : 0; } @@ -673,6 +716,44 @@ colormapFromBv(unsigned int const colorCt, static void +setBox(struct Box * const boxP, + unsigned int const startIndex, + unsigned int const colorCt, + unsigned int const sum, + struct BoxVector * const boxVectorP, + enum MethodForLargest const methodForLargest + ) { + + boxP->startIndex = startIndex; + boxP->colorCt = colorCt; + boxP->sum = sum; + + computeBoxSpread(boxP, boxVectorP->colorFreqTable, + boxVectorP->colorDepth, methodForLargest, + &boxP->maxdim, &boxP->spread); +} + + + +static void +makeNewBox(struct BoxVector * const boxVectorP, + unsigned int const startIndex, + unsigned int const colorCt, + unsigned int const sum, + enum MethodForLargest const methodForLargest) { + + struct Box * const boxP = &boxVectorP->box[boxVectorP->boxCt++]; + + assert(boxVectorP->boxCt <= boxVectorP->capacity); + + boxP->serialNum = boxVectorP->boxCt; + + setBox(boxP, startIndex, colorCt, sum, boxVectorP, methodForLargest); +} + + + +static void splitBox(struct BoxVector * const boxVectorP, unsigned int const boxIdx, enum MethodForLargest const methodForLargest, @@ -693,19 +774,14 @@ splitBox(struct BoxVector * const boxVectorP, unsigned int lowerSum; /* Number of pixels whose value is "less than" the median */ - - /* Perhaps this sort should go after creating a box, not before splitting. - Because you need the sort to use the REP_CENTER_BOX method of choosing - a color to represent the final boxes - */ - - /* Set the gross global variable 'compareplanePlane' as a - parameter to compareplane(), which is called by qsort(). + /* Set the gross global variable 'compareColorParm' as a + parameter to compareColor(), which is called by qsort(). */ - compareplanePlane = boxVectorP->box[boxIdx].maxdim; + compareColorParm.comparePlane = boxVectorP->box[boxIdx].maxdim; + compareColorParm.colorDepth = boxVectorP->colorDepth; qsort((char*) &boxVectorP->colorFreqTable.table[boxStart], boxSize, sizeof(boxVectorP->colorFreqTable.table[boxStart]), - compareplane); + compareColor); { /* Find the median based on the counts, so that about half the pixels @@ -720,27 +796,18 @@ splitBox(struct BoxVector * const boxVectorP, } medianIndex = i; } - /* Split the box, and sort to bring the biggest boxes to the top. */ - { - struct Box * const oldBoxP = &boxVectorP->box[boxIdx]; + /* Split the box, and sort to bring the biggest boxes to the top. The old + box becomes the lower half; we make a new box for the upper half. + */ + setBox(&boxVectorP->box[boxIdx], + boxStart, medianIndex, + lowerSum, + boxVectorP, methodForLargest); - oldBoxP->colorCt = medianIndex; - oldBoxP->sum = lowerSum; - computeBoxSpread(oldBoxP, boxVectorP->colorFreqTable, - boxVectorP->colorDepth, methodForLargest, - &oldBoxP->maxdim, &oldBoxP->spread); - } - { - struct Box * const newBoxP = &boxVectorP->box[boxVectorP->boxCt]; - - newBoxP->startIndex = boxStart + medianIndex; - newBoxP->colorCt = boxSize - medianIndex; - newBoxP->sum = sum - lowerSum; - computeBoxSpread(newBoxP, boxVectorP->colorFreqTable, - boxVectorP->colorDepth, methodForLargest, - &newBoxP->maxdim, &newBoxP->spread); - ++boxVectorP->boxCt; - } + makeNewBox(boxVectorP, + boxStart + medianIndex, boxSize - medianIndex, + sum - lowerSum, + methodForLargest); sortBoxes(boxVectorP, methodForSplit); } @@ -1183,7 +1250,7 @@ main(int argc, const char * argv[] ) { cmdline.methodForLargest, cmdline.methodForRep, cmdline.methodForSplit, - cmdline.debug, + !!cmdline.debug, &format, &colormapPam, &colormap); pm_close(ifP); |