From 8606c4e85e16ff97366511b472b96d993dd2711a Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 7 Sep 2019 21:18:02 +0000 Subject: cleanup git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@3674 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- other/pnmcolormap.c | 490 +++++++++++++++++++++++++++++----------------------- 1 file changed, 273 insertions(+), 217 deletions(-) (limited to 'other') diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c index 7b644572..7da3122b 100644 --- a/other/pnmcolormap.c +++ b/other/pnmcolormap.c @@ -34,36 +34,41 @@ #include "pam.h" #include "pammap.h" -enum methodForLargest {LARGE_NORM, LARGE_LUM}; +enum MethodForLargest {LARGE_NORM, LARGE_LUM}; -enum methodForRep {REP_CENTER_BOX, REP_AVERAGE_COLORS, REP_AVERAGE_PIXELS}; +enum MethodForRep {REP_CENTER_BOX, REP_AVERAGE_COLORS, REP_AVERAGE_PIXELS}; -enum methodForSplit {SPLIT_MAX_PIXELS, SPLIT_MAX_COLORS, SPLIT_MAX_SPREAD}; +enum MethodForSplit {SPLIT_MAX_PIXELS, SPLIT_MAX_COLORS, SPLIT_MAX_SPREAD}; -typedef struct box* boxVector; -struct box { - int ind; - int colors; - int sum; +struct Box { + unsigned int index; + unsigned int colorCt; + unsigned int sum; unsigned int maxdim; /* which dimension has the largest spread. RGB plane number. */ sample spread; /* spread in dimension 'maxdim' */ }; -struct cmdlineInfo { +struct BoxVector { + struct Box * box; /* malloc'ed array */ + unsigned int boxCt; + unsigned int capacity; +}; + +struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char *inputFilespec; /* Filespec of input file */ + const char * inputFileNm; /* Name of input file */ unsigned int allcolors; /* boolean: select all colors from the input */ unsigned int newcolors; /* Number of colors argument; meaningless if allcolors true */ - enum methodForLargest methodForLargest; + enum MethodForLargest methodForLargest; /* -spreadintensity/-spreadluminosity options */ - enum methodForRep methodForRep; + enum MethodForRep methodForRep; /* -center/-meancolor/-meanpixel options */ - enum methodForSplit methodForSplit; + enum MethodForSplit methodForSplit; /* -splitpixelct/-splitcolorct/-splitspread options */ unsigned int sort; unsigned int square; @@ -73,8 +78,8 @@ struct cmdlineInfo { static void -parseCommandLine (int argc, char ** argv, - struct cmdlineInfo *cmdlineP) { +parseCommandLine (int argc, const char ** argv, + struct CmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. @@ -126,7 +131,7 @@ parseCommandLine (int argc, char ** argv, 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 *cmdline_p and others. */ @@ -163,9 +168,9 @@ parseCommandLine (int argc, char ** argv, "You specified %d arguments.", argc-1); else { if (argc-1 < 2) - cmdlineP->inputFilespec = "-"; + cmdlineP->inputFileNm = "-"; else - cmdlineP->inputFilespec = argv[2]; + cmdlineP->inputFileNm = argv[2]; if (argc-1 < 1) pm_error("You must specify the number of colors in the " @@ -210,8 +215,13 @@ compareplane(const void * const arg1, const struct tupleint * const * const comparandPP = arg1; const struct tupleint * const * const comparatorPP = arg2; - return (*comparandPP)->tuple[compareplanePlane] - - (*comparatorPP)->tuple[compareplanePlane]; + sample const comparandSample = (*comparandPP) ->tuple[compareplanePlane]; + sample const comparatorSample = (*comparatorPP)->tuple[compareplanePlane]; + + return + comparandSample < comparatorSample ? -1 : + comparandSample > comparatorSample ? 1 : + 0; } @@ -221,8 +231,16 @@ static qsort_comparison_fn sumcompare; #endif static int -sumcompare(const void * const b1, const void * const b2) { - return(((boxVector)b2)->sum - ((boxVector)b1)->sum); +sumcompare(const void * const arg1, + const void * const arg2) { + + struct Box * const comparandP = (struct Box *)arg1; + struct Box * const comparatorP = (struct Box *)arg2; + + return + comparatorP->sum < comparandP->sum ? -1 : + comparatorP->sum > comparandP->sum ? 1 : + 0; } @@ -232,8 +250,16 @@ static qsort_comparison_fn colcompare; #endif static int -colcompare(const void * const b1, const void * const b2) { - return(((boxVector)b2)->colors - ((boxVector)b1)->colors); +colcompare(const void * const arg1, + const void * const arg2) { + + struct Box * const comparandP = (struct Box *)arg1; + struct Box * const comparatorP = (struct Box *)arg2; + + return + comparatorP->colorCt < comparandP->colorCt ? -1 : + comparatorP->colorCt > comparandP->colorCt ? 1 : + 0; } @@ -243,35 +269,34 @@ static qsort_comparison_fn spreadcompare; #endif static int -spreadcompare(const void * const b1, const void * const b2) { +spreadcompare(const void * const arg1, + const void * const arg2) { - boxVector const box1P = (boxVector)(b1); - boxVector const box2P = (boxVector)(b2); + struct Box * const comparandP = (struct Box *)arg1; + struct Box * const comparatorP = (struct Box *)arg2; return - box2P->spread > box1P->spread ? 1 : - box2P->spread < box1P->spread ? -1 : + comparatorP->spread < comparandP->spread ? -1 : + comparatorP->spread > comparandP->spread ? 1 : 0; } static void -sortBoxes(boxVector const bv, - unsigned int const boxCt, - enum methodForSplit const methodForSplit) { +sortBoxes(struct BoxVector * const boxVectorP, + enum MethodForSplit const methodForSplit) { + + qsort_comparison_fn * comparisonFn; switch (methodForSplit){ - case SPLIT_MAX_PIXELS: - qsort((char*) bv, boxCt, sizeof(struct box), sumcompare); - break; - case SPLIT_MAX_COLORS: - qsort((char*) bv, boxCt, sizeof(struct box), colcompare); - break; - case SPLIT_MAX_SPREAD: - qsort((char*) bv, boxCt, sizeof(struct box), spreadcompare); - break; + case SPLIT_MAX_PIXELS: comparisonFn = &sumcompare; break; + case SPLIT_MAX_COLORS: comparisonFn = &colcompare; break; + case SPLIT_MAX_SPREAD: comparisonFn = &spreadcompare; break; } + + qsort((char*) &boxVectorP->box[0], boxVectorP->boxCt, sizeof(struct Box), + comparisonFn); } @@ -282,48 +307,6 @@ sortBoxes(boxVector const bv, ** Display", SIGGRAPH '82 Proceedings, page 297. */ -static tupletable2 -newColorMap(unsigned int const newcolors, - unsigned int const depth) { - - tupletable2 colormap; - unsigned int i; - struct pam pam; - - pam.depth = depth; - - colormap.table = pnm_alloctupletable(&pam, newcolors); - - for (i = 0; i < newcolors; ++i) { - unsigned int plane; - for (plane = 0; plane < depth; ++plane) - colormap.table[i]->tuple[plane] = 0; - } - colormap.size = newcolors; - - return colormap; -} - - - -static boxVector -newBoxVector(int const colors, int const sum, int const newcolors) { - - boxVector bv; - MALLOCARRAY(bv, newcolors); - if (bv == NULL) - pm_error("out of memory allocating box vector table"); - - /* Set up the initial box. */ - bv[0].ind = 0; - bv[0].colors = colors; - bv[0].sum = sum; - - return bv; -} - - - static void findBoxBoundaries(tupletable2 const colorfreqtable, unsigned int const depth, @@ -332,8 +315,8 @@ findBoxBoundaries(tupletable2 const colorfreqtable, sample minval[], sample maxval[]) { /*---------------------------------------------------------------------------- - Go through the box finding the minimum and maximum of each - component - the boundaries of the box. + Go through the box finding the minimum and maximum of each component - the + boundaries of the box. -----------------------------------------------------------------------------*/ unsigned int plane; unsigned int i; @@ -417,6 +400,100 @@ findPlaneWithLargestSpreadByLuminosity(sample const minval[], +static void +computeBoxSpread(const struct Box * const boxP, + tupletable2 const colorfreqtable, + unsigned int const depth, + enum MethodForLargest const methodForLargest, + unsigned int * const planeWithLargestP, + sample * const spreadP + ) { +/*---------------------------------------------------------------------------- + Find the spread in the dimension in which it is greatest. + + Return as *planeWithLargestP the number of that plane and as *spreadP the + spread in that plane. +-----------------------------------------------------------------------------*/ + sample * minval; /* malloc'ed array */ + sample * maxval; /* malloc'ed array */ + + MALLOCARRAY_NOFAIL(minval, depth); + MALLOCARRAY_NOFAIL(maxval, depth); + + findBoxBoundaries(colorfreqtable, depth, boxP->index, boxP->colorCt, + minval, maxval); + + switch (methodForLargest) { + case LARGE_NORM: + findPlaneWithLargestSpreadByNorm(minval, maxval, depth, + planeWithLargestP, spreadP); + break; + case LARGE_LUM: + findPlaneWithLargestSpreadByLuminosity(minval, maxval, depth, + planeWithLargestP, spreadP); + break; + } + free(minval); free(maxval); +} + + + +static unsigned int +freqTotal(tupletable2 const freqtable) { + + unsigned int i; + unsigned int sum; + + for (i = 0, sum = 0; i < freqtable.size; ++i) + sum += freqtable.table[i]->value; + + return sum; +} + + + +static struct BoxVector +newBoxVector(tupletable2 const colorfreqtable, + unsigned int const capacity, + unsigned int const depth, + enum MethodForLargest const methodForLargest) { + + unsigned int const colorCt = colorfreqtable.size; + unsigned int const sum = freqTotal(colorfreqtable); + + struct BoxVector boxVector; + + MALLOCARRAY(boxVector.box, capacity); + + if (!boxVector.box) + pm_error("out of memory allocating box vector table"); + + /* Set up the initial box. */ + boxVector.box[0].index = 0; + boxVector.box[0].colorCt = colorCt; + boxVector.box[0].sum = sum; + + computeBoxSpread(&boxVector.box[0], colorfreqtable, depth, + methodForLargest, + &boxVector.box[0].maxdim, + &boxVector.box[0].spread); + + boxVector.boxCt = 1; + boxVector.capacity = capacity; + + return boxVector; +} + + + +static void +destroyBoxVector(struct BoxVector const boxVector) { + + free(boxVector.box); +} + + + static void centerBox(int const boxStart, int const boxSize, @@ -443,6 +520,30 @@ centerBox(int const boxStart, +static tupletable2 +newColorMap(unsigned int const colorCt, + unsigned int const depth) { + + tupletable2 colormap; + unsigned int i; + struct pam pam; + + pam.depth = depth; + + colormap.table = pnm_alloctupletable(&pam, colorCt); + + for (i = 0; i < colorCt; ++i) { + unsigned int plane; + for (plane = 0; plane < depth; ++plane) + colormap.table[i]->tuple[plane] = 0; + } + colormap.size = colorCt; + + return colormap; +} + + + static void averageColors(int const boxStart, int const boxSize, @@ -502,12 +603,11 @@ averagePixels(int const boxStart, static tupletable2 -colormapFromBv(unsigned int const newcolors, - boxVector const bv, - unsigned int const boxes, +colormapFromBv(unsigned int const colorCt, + struct BoxVector const boxVector, tupletable2 const colorfreqtable, unsigned int const depth, - enum methodForRep const methodForRep) { + enum MethodForRep const methodForRep) { /* ** Ok, we've got enough boxes. Now choose a representative color for ** each box. There are a number of possible ways to make this choice. @@ -517,23 +617,29 @@ colormapFromBv(unsigned int const newcolors, ** method is to average all the pixels in the box. */ tupletable2 colormap; - unsigned int bi; + unsigned int boxIdx; - colormap = newColorMap(newcolors, depth); + colormap = newColorMap(colorCt, depth); - for (bi = 0; bi < boxes; ++bi) { + for (boxIdx = 0; boxIdx < boxVector.boxCt; ++boxIdx) { switch (methodForRep) { case REP_CENTER_BOX: - centerBox(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, - colormap.table[bi]->tuple); + centerBox(boxVector.box[boxIdx].index, + boxVector.box[boxIdx].colorCt, + colorfreqtable, depth, + colormap.table[boxIdx]->tuple); break; case REP_AVERAGE_COLORS: - averageColors(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, - colormap.table[bi]->tuple); + averageColors(boxVector.box[boxIdx].index, + boxVector.box[boxIdx].colorCt, + colorfreqtable, depth, + colormap.table[boxIdx]->tuple); break; case REP_AVERAGE_PIXELS: - averagePixels(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, - colormap.table[bi]->tuple); + averagePixels(boxVector.box[boxIdx].index, + boxVector.box[boxIdx].colorCt, + colorfreqtable, depth, + colormap.table[boxIdx]->tuple); break; default: pm_error("Internal error: invalid value of methodForRep: %d", @@ -545,126 +651,77 @@ colormapFromBv(unsigned int const newcolors, -static unsigned int -freqTotal(tupletable2 const freqtable) { - - unsigned int i; - unsigned int sum; - - sum = 0; - - for (i = 0; i < freqtable.size; ++i) - sum += freqtable.table[i]->value; - - return sum; -} - - - static void -computeBoxSpread(const struct box * const boxP, - tupletable2 const colorfreqtable, - unsigned int const depth, - enum methodForLargest const methodForLargest, - unsigned int * const planeWithLargestP, - sample * const spreadP - ) { -/*---------------------------------------------------------------------------- - Find the spread in the dimension in which it is greatest. - - Return as *planeWithLargestP the number of that plane and as *spreadP the - spread in that plane. ------------------------------------------------------------------------------*/ - sample * minval; /* malloc'ed array */ - sample * maxval; /* malloc'ed array */ - - MALLOCARRAY_NOFAIL(minval, depth); - MALLOCARRAY_NOFAIL(maxval, depth); - - findBoxBoundaries(colorfreqtable, depth, boxP->ind, boxP->colors, - minval, maxval); - - switch (methodForLargest) { - case LARGE_NORM: - findPlaneWithLargestSpreadByNorm(minval, maxval, depth, - planeWithLargestP, spreadP); - break; - case LARGE_LUM: - findPlaneWithLargestSpreadByLuminosity(minval, maxval, depth, - planeWithLargestP, spreadP); - break; - } - free(minval); free(maxval); -} - - - -static void -splitBox(boxVector const bv, - unsigned int * const boxesP, - unsigned int const bi, +splitBox(struct BoxVector * const boxVectorP, + unsigned int const boxIdx, tupletable2 const colorfreqtable, unsigned int const depth, - enum methodForLargest const methodForLargest, - enum methodForSplit const methodForSplit) { + enum MethodForLargest const methodForLargest, + enum MethodForSplit const methodForSplit) { /*---------------------------------------------------------------------------- - Split Box 'bi' in the box vector bv (so that bv contains one more box - than it did as input). Split it so that each new box represents about - half of the pixels in the distribution given by 'colorfreqtable' for - the colors in the original box, but with distinct colors in each of the - two new boxes. + Split Box 'boxIdx' in the box vector 'boxVector' (so that bv contains one + more box than it did as input). Split it so that each new box represents + about half of the pixels in the distribution given by 'colorfreqtable' for + the colors in the original box, but with distinct colors in each of the two + new boxes. Assume the box contains at least two colors. -----------------------------------------------------------------------------*/ - unsigned int const boxStart = bv[bi].ind; - unsigned int const boxSize = bv[bi].colors; - unsigned int const sm = bv[bi].sum; + unsigned int const boxStart = boxVectorP->box[boxIdx].index; + unsigned int const boxSize = boxVectorP->box[boxIdx].colorCt; + unsigned int const sum = boxVectorP->box[boxIdx].sum; unsigned int medianIndex; int lowersum; /* Number of pixels whose value is "less than" the median */ - /* TODO: I think 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 + /* 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(). */ - compareplanePlane = bv[bi].maxdim; + compareplanePlane = boxVectorP->box[boxIdx].maxdim; qsort((char*) &colorfreqtable.table[boxStart], boxSize, sizeof(colorfreqtable.table[boxStart]), compareplane); { - /* Now find the median based on the counts, so that about half - the pixels (not colors, pixels) are in each subdivision. */ - + /* Find the median based on the counts, so that about half the pixels + (not colors, pixels) are in each subdivision. + */ unsigned int i; lowersum = colorfreqtable.table[boxStart]->value; /* initial value */ - for (i = 1; i < boxSize - 1 && lowersum < sm/2; ++i) { + for (i = 1; i < boxSize - 1 && lowersum < sum/2; ++i) { lowersum += colorfreqtable.table[boxStart + i]->value; } medianIndex = i; } /* Split the box, and sort to bring the biggest boxes to the top. */ - bv[bi].colors = medianIndex; - bv[bi].sum = lowersum; - computeBoxSpread(&bv[bi], colorfreqtable, depth, methodForLargest, - &bv[bi].maxdim, &bv[bi].spread); - - bv[*boxesP].ind = boxStart + medianIndex; - bv[*boxesP].colors = boxSize - medianIndex; - bv[*boxesP].sum = sm - lowersum; - computeBoxSpread(&bv[*boxesP], colorfreqtable, depth, methodForLargest, - &bv[*boxesP].maxdim, &bv[*boxesP].spread); - ++(*boxesP); - - sortBoxes(bv, *boxesP, methodForSplit); + { + struct Box * const oldBoxP = &boxVectorP->box[boxIdx]; + + oldBoxP->colorCt = medianIndex; + oldBoxP->sum = lowersum; + computeBoxSpread(oldBoxP, colorfreqtable, depth, methodForLargest, + &oldBoxP->maxdim, &oldBoxP->spread); + } + { + struct Box * const newBoxP = &boxVectorP->box[boxVectorP->boxCt]; + + newBoxP->index = boxStart + medianIndex; + newBoxP->colorCt = boxSize - medianIndex; + newBoxP->sum = sum - lowersum; + computeBoxSpread(newBoxP, colorfreqtable, depth, methodForLargest, + &newBoxP->maxdim, &newBoxP->spread); + ++boxVectorP->boxCt; + } + + sortBoxes(boxVectorP, methodForSplit); } @@ -672,13 +729,13 @@ splitBox(boxVector const bv, static void mediancut(tupletable2 const colorfreqtable, unsigned int const depth, - int const newcolors, - enum methodForLargest const methodForLargest, - enum methodForRep const methodForRep, - enum methodForSplit const methodForSplit, + int const newcolorCt, + enum MethodForLargest const methodForLargest, + enum MethodForRep const methodForRep, + enum MethodForSplit const methodForSplit, tupletable2 * const colormapP) { /*---------------------------------------------------------------------------- - Compute a set of only 'newcolors' colors that best represent an + Compute a set of only 'newcolorCt' colors that best represent an image whose pixels are summarized by the histogram 'colorfreqtable'. Each tuple in that table has depth 'depth'. colorfreqtable.table[i] tells the number of pixels in the subject image @@ -686,37 +743,36 @@ mediancut(tupletable2 const colorfreqtable, As a side effect, sort 'colorfreqtable'. -----------------------------------------------------------------------------*/ - boxVector bv; - unsigned int bi; - unsigned int boxes; + struct BoxVector boxVector; bool multicolorBoxesExist; /* There is at least one box that contains at least 2 colors; ergo, there is more splitting we can do. */ - bv = newBoxVector(colorfreqtable.size, freqTotal(colorfreqtable), - newcolors); + boxVector = newBoxVector(colorfreqtable, newcolorCt, depth, + methodForLargest); - computeBoxSpread(&bv[0], colorfreqtable, depth, methodForLargest, - &bv[0].maxdim, &bv[0].spread); - - boxes = 1; multicolorBoxesExist = (colorfreqtable.size > 1); - /* Main loop: split boxes until we have enough. */ - while (boxes < newcolors && multicolorBoxesExist) { - /* Find the first splittable box. */ - for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi); - if (bi >= boxes) + /* Split boxes until we have enough. */ + while (boxVector.boxCt < newcolorCt && multicolorBoxesExist) { + unsigned int boxIdx; + + for (boxIdx = 0; + boxIdx < boxVector.boxCt && boxVector.box[boxIdx].colorCt < 2; + ++boxIdx); + /* Find the first splittable box. */ + + if (boxIdx >= boxVector.boxCt) multicolorBoxesExist = FALSE; else - splitBox(bv, &boxes, bi, colorfreqtable, depth, + splitBox(&boxVector, boxIdx, colorfreqtable, depth, methodForLargest, methodForSplit); } - *colormapP = colormapFromBv(newcolors, bv, boxes, colorfreqtable, depth, - methodForRep); + *colormapP = colormapFromBv(newcolorCt, boxVector, colorfreqtable, + depth, methodForRep); - free(bv); + destroyBoxVector(boxVector); } @@ -844,9 +900,9 @@ static void computeColorMapFromInput(FILE * const ifP, bool const allColors, int const reqColors, - enum methodForLargest const methodForLargest, - enum methodForRep const methodForRep, - enum methodForSplit const methodForSplit, + enum MethodForLargest const methodForLargest, + enum MethodForRep const methodForRep, + enum MethodForSplit const methodForSplit, int * const formatP, struct pam * const freqPamP, tupletable2 * const colormapP) { @@ -1039,9 +1095,9 @@ colormapToImage(int const format, int -main(int argc, char * argv[] ) { +main(int argc, const char * argv[] ) { - struct cmdlineInfo cmdline; + struct CmdlineInfo cmdline; FILE * ifP; int format; struct pam colormapPam; @@ -1049,11 +1105,11 @@ main(int argc, char * argv[] ) { tuple ** colormapRaster; tupletable2 colormap; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - ifP = pm_openr(cmdline.inputFilespec); + ifP = pm_openr(cmdline.inputFileNm); computeColorMapFromInput(ifP, cmdline.allcolors, cmdline.newcolors, -- cgit 1.4.1