diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2009-09-27 21:44:29 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2009-09-27 21:44:29 +0000 |
commit | 43939e66b1d4eeb2f3799c124f3598756755005a (patch) | |
tree | 15733092de55d52421a6ea02f5a43d5f8ff24393 /editor/pnmremap.c | |
parent | 49f4336c9bba33650573ba780b70bc501b38643e (diff) | |
download | netpbm-mirror-43939e66b1d4eeb2f3799c124f3598756755005a.tar.gz netpbm-mirror-43939e66b1d4eeb2f3799c124f3598756755005a.tar.xz netpbm-mirror-43939e66b1d4eeb2f3799c124f3598756755005a.zip |
Rebase Stable series to current Advanced: 10.47.04
git-svn-id: http://svn.code.sf.net/p/netpbm/code/stable@995 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor/pnmremap.c')
-rw-r--r-- | editor/pnmremap.c | 548 |
1 files changed, 406 insertions, 142 deletions
diff --git a/editor/pnmremap.c b/editor/pnmremap.c index 1ed07fdb..5b58addb 100644 --- a/editor/pnmremap.c +++ b/editor/pnmremap.c @@ -40,15 +40,6 @@ enum missingMethod { MISSING_CLOSE }; -#define FS_SCALE 1024 - -struct fserr { - long** thiserr; - long** nexterr; - bool fsForward; -}; - - struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. @@ -56,6 +47,7 @@ struct cmdlineInfo { const char * inputFilespec; /* Filespec of input file */ const char * mapFilespec; /* Filespec of colormap file */ unsigned int floyd; /* Boolean: -floyd/-fs option */ + unsigned int norandom; enum missingMethod missingMethod; char * missingcolor; /* -missingcolor value. Null if not specified */ @@ -98,6 +90,8 @@ parseCommandLine (int argc, char ** argv, NULL, &nofloyd, 0); OPTENT3(0, "nofs", OPT_FLAG, NULL, &nofloyd, 0); + OPTENT3(0, "norandom", OPT_FLAG, + NULL, &cmdlineP->norandom, 0); OPTENT3(0, "firstisdefault", OPT_FLAG, NULL, &firstisdefault, 0); OPTENT3(0, "mapfile", OPT_STRING, @@ -143,53 +137,112 @@ parseCommandLine (int argc, char ** argv, } +typedef enum { + ADJUST_NONE, + ADJUST_RGBTO1, + ADJUST_GRAYSCALETO3 +} depthAdjustment; + + static void -rgbToDepth1(const struct pam * const pamP, - tuple * const tupleRow) { - - unsigned int col; +rgbToDepth1(tuple const tuple) { - for (col = 0; col < pamP->width; ++col) { - unsigned int plane; - double grayvalue; - grayvalue = 0.0; /* initial value */ - for (plane = 0; plane < pamP->depth; ++plane) - grayvalue += pnm_lumin_factor[plane] * tupleRow[col][plane]; - tupleRow[col][0] = (sample) (grayvalue + 0.5); - } + unsigned int plane; + double grayvalue; + + grayvalue = 0.0; /* initial value */ + + for (plane = 0; plane < 3; ++plane) + grayvalue += pnm_lumin_factor[plane] * tuple[plane]; + + tuple[0] = (sample) (grayvalue + 0.5); } static void -grayscaleToDepth3(const struct pam * const pamP, - tuple * const tupleRow) { +grayscaleToDepth3(tuple const tuple) { + + tuple[1] = tuple[0]; + tuple[2] = tuple[0]; +} + + + +static void +adjustDepthTuple(tuple const tuple, + depthAdjustment const adjustment) { - unsigned int col; + switch (adjustment) { + case ADJUST_NONE: + break; + case ADJUST_RGBTO1: + rgbToDepth1(tuple); + break; + case ADJUST_GRAYSCALETO3: + grayscaleToDepth3(tuple); + break; + } +} + - assert(pamP->allocation_depth >= 3); - for (col = 0; col < pamP->width; ++col) { - tupleRow[col][1] = tupleRow[col][0]; - tupleRow[col][2] = tupleRow[col][0]; +static void +inverseAdjustDepthTuple(tuple const tuple, + depthAdjustment const adjustment) { + + switch (adjustment) { + case ADJUST_NONE: + break; + case ADJUST_RGBTO1: + grayscaleToDepth3(tuple); + break; + case ADJUST_GRAYSCALETO3: + rgbToDepth1(tuple); + break; } } static void -adjustDepth(const struct pam * const pamP, - tuple * const tupleRow, - unsigned int const newDepth) { +adjustDepthRow(tuple * const tupleRow, + unsigned int const width, + depthAdjustment const adjustment) { /*---------------------------------------------------------------------------- - Change the depth of the raster row tupleRow[] of the image - described by 'pamP' to newDepth. + Change tupleRow[] depth as indicated by 'adjustment', + i.e. turned from RGB to grayscale or grayscale to RGB. - We don't change the memory allocation; tupleRow[] must already have - space allocated for at least 'newDepth' planes. When we're done, - all but the first 'newDepth' planes are meaningless, but the space is - still there. + We assume tupleRow[] is consistent with 'adjustment' -- i.e. if + 'adjustment' says grayscale to RGB, tupleRow[] has an allocation depth of + at least 3 and if 'adjustment' says from RGB to grayscale, tupleRow[] has + RGB tuples. +-----------------------------------------------------------------------------*/ + if (adjustment == ADJUST_NONE) { + } else { + unsigned int col; + + for (col = 0; col < width; ++col) { + if (adjustment == ADJUST_RGBTO1) + rgbToDepth1(tupleRow[col]); + else { + assert(adjustment == ADJUST_GRAYSCALETO3); + grayscaleToDepth3(tupleRow[col]); + } + } + } +} + + + +static void +selectDepthAdjustment(const struct pam * const pamP, + unsigned int const newDepth, + depthAdjustment * const adjustmentP) { +/*---------------------------------------------------------------------------- + Determine what kind of depth adjustment the pixels of an image described + by 'pamP' need to be comparable to pixels with depth 'newDepth'. The only depth changes we know how to do are: @@ -197,15 +250,23 @@ adjustDepth(const struct pam * const pamP, We change it to grayscale or black and white. + For this, we return *adjustmentP == ADJUST_RGBTO1. + - from tuple type GRAYSCALE or BLACKANDWHITE depth 1 to depth 3. We change it to RGB. + For this, we return *adjustmentP == ADJUST_GRAYSCALETO3. + For any other depth change request, we issue an error message and abort the program. ------------------------------------------------------------------------------*/ - if (newDepth != pamP->depth) { + If 'newDepth' is the same depth as the original (no depth change required), + we return *adjustmentP == ADJUST_NONE. +-----------------------------------------------------------------------------*/ + if (newDepth == pamP->depth) + *adjustmentP = ADJUST_NONE; + else { if (stripeq(pamP->tuple_type, "RGB")) { if (newDepth != 1) { pm_error("Map image depth of %u differs from input image " @@ -214,7 +275,7 @@ adjustDepth(const struct pam * const pamP, "an RGB tuple is 1.", newDepth, pamP->depth); } else - rgbToDepth1(pamP, tupleRow); + *adjustmentP = ADJUST_RGBTO1; } else if (stripeq(pamP->tuple_type, "GRAYSCALE") || stripeq(pamP->tuple_type, "BLACKANDWHITE")) { if (newDepth != 3) { @@ -225,7 +286,7 @@ adjustDepth(const struct pam * const pamP, "a GRAYSCALE or BLACKANDWHITE tuple is 3.", newDepth, pamP->depth); } else - grayscaleToDepth3(pamP, tupleRow); + *adjustmentP = ADJUST_GRAYSCALETO3; } else { pm_error("Map image depth of %u differs from input image depth " "of %u, and the input image does not have a tuple type " @@ -240,7 +301,6 @@ adjustDepth(const struct pam * const pamP, - static void computeColorMapFromMap(struct pam * const mappamP, tuple ** const maptuples, @@ -263,15 +323,85 @@ computeColorMapFromMap(struct pam * const mappamP, pnm_computetuplefreqtable(mappamP, maptuples, MAXCOLORS, &colors); if (*colormapP == NULL) pm_error("too many colors in colormap!"); - pm_message("%d colors found in colormap", colors); + pm_message("%u colors found in colormap", colors); *newcolorsP = colors; } +#define FS_SCALE 1024 + +struct fserr { + unsigned int width; + /* Width of the image being dithered */ + long ** thiserr; + long ** nexterr; + bool fsForward; + /* We are in a left-to-right row */ + int begCol; + /* First column in the sweep. Determined by 'fsForward': either + the leftmost or rightmost column in the row + */ + int endCol; + /* Column after the last column in the sweep. Determined by + 'fsForward': either one past the left end or one past the right + end of the row. + */ + int step; + /* What we add to a column number to get the next one in the sweep. + Determined by 'fsForward': +1 or -1. + */ +}; + + + +static void +randomizeError(long ** const err, + unsigned int const width, + unsigned int const depth) { +/*---------------------------------------------------------------------------- + Set a random error in the range [-1 .. 1] (normalized via FS_SCALE) + in the error array err[][]. +-----------------------------------------------------------------------------*/ + unsigned int col; + + srand(pm_randseed()); + + for (col = 0; col < width; ++col) { + unsigned int plane; + for (plane = 0; plane < depth; ++plane) + err[plane][col] = rand() % (FS_SCALE * 2) - FS_SCALE; + } +} + + + +static void +fserrSetForward(struct fserr * const fserrP) { + + fserrP->fsForward = TRUE; + fserrP->begCol = 0; + fserrP->endCol = fserrP->width; + fserrP->step = +1; +} + + + +static void +fserrSetBackward(struct fserr * const fserrP) { + + fserrP->fsForward = FALSE; + fserrP->begCol = fserrP->width - 1; + fserrP->endCol = -1; + fserrP->step = -1; +} + + + static void initFserr(struct pam * const pamP, - struct fserr * const fserrP) { + struct fserr * const fserrP, + bool const initRandom) { /*---------------------------------------------------------------------------- Initialize the Floyd-Steinberg error vectors -----------------------------------------------------------------------------*/ @@ -279,6 +409,8 @@ initFserr(struct pam * const pamP, unsigned int const fserrSize = pamP->width + 2; + fserrP->width = pamP->width; + MALLOCARRAY(fserrP->thiserr, pamP->depth); if (fserrP->thiserr == NULL) pm_error("Out of memory allocating Floyd-Steinberg structures " @@ -299,20 +431,10 @@ initFserr(struct pam * const pamP, "for Plane %u, size %u", plane, fserrSize); } - srand((int)(time(0) ^ getpid())); + if (initRandom) + randomizeError(fserrP->thiserr, fserrSize, pamP->depth); - { - int col; - - for (col = 0; col < fserrSize; ++col) { - unsigned int plane; - for (plane = 0; plane < pamP->depth; ++plane) - fserrP->thiserr[plane][col] = - rand() % (FS_SCALE * 2) - FS_SCALE; - /* (random errors in [-1 .. 1]) */ - } - } - fserrP->fsForward = TRUE; + fserrSetForward(fserrP); } @@ -333,7 +455,8 @@ floydInitRow(struct pam * const pamP, struct fserr * const fserrP) { static void floydAdjustColor(struct pam * const pamP, - tuple const tuple, + tuple const intuple, + tuple const outtuple, struct fserr * const fserrP, int const col) { /*---------------------------------------------------------------------------- @@ -343,8 +466,8 @@ floydAdjustColor(struct pam * const pamP, for (plane = 0; plane < pamP->depth; ++plane) { long int const s = - tuple[plane] + fserrP->thiserr[plane][col+1] / FS_SCALE; - tuple[plane] = MIN(pamP->maxval, MAX(0,s)); + intuple[plane] + fserrP->thiserr[plane][col+1] / FS_SCALE; + outtuple[plane] = MIN(pamP->maxval, MAX(0,s)); } } @@ -396,7 +519,11 @@ floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) { fserrP->thiserr[plane] = fserrP->nexterr[plane]; fserrP->nexterr[plane] = temperr; } - fserrP->fsForward = ! fserrP->fsForward; + + if (fserrP->fsForward) + fserrSetBackward(fserrP); + else + fserrSetForward(fserrP); } @@ -616,21 +743,106 @@ lookupThroughHash(struct pam * const pamP, static void -convertRow(struct pam * const pamP, - tuple tuplerow[], - tupletable const colormap, - struct colormapFinder * const colorFinderP, - tuplehash const colorhash, - bool * const usehashP, - bool const floyd, - tuple const defaultColor, - struct fserr * const fserrP, - unsigned int * const missingCountP) { +mapTuple(struct pam * const pamP, + tuple const inTuple, + tuple const defaultColor, + tupletable const colormap, + struct colormapFinder * const colorFinderP, + tuplehash const colorhash, + bool * const usehashP, + tuple const outTuple, + bool * const missingP) { + + int colormapIndex; + /* Index into the colormap of the replacement color, or -1 if + there is no usable color in the color map. + */ + + lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP, + colorhash, &colormapIndex, usehashP); + + if (colormapIndex == -1) { + *missingP = TRUE; + + assert(defaultColor); /* Otherwise, lookup would have succeeded */ + + pnm_assigntuple(pamP, outTuple, defaultColor); + } else { + *missingP = FALSE; + pnm_assigntuple(pamP, outTuple, colormap[colormapIndex]->tuple); + } +} + + + +static void +convertRowStraight(struct pam * const inpamP, + struct pam * const outpamP, + tuple inrow[], + depthAdjustment const depthAdjustment, + tupletable const colormap, + struct colormapFinder * const colorFinderP, + tuplehash const colorhash, + bool * const usehashP, + tuple const defaultColor, + tuple outrow[], + unsigned int * const missingCountP) { /*---------------------------------------------------------------------------- - Replace the colors in row tuplerow[] (described by *pamP) with the - new colors. + Like convertRow(), compute outrow[] from inrow[], replacing each pixel with + the new colors. Do a straight pixel for pixel remap; no dithering. + + Return the number of pixels that were not matched in the color map as + *missingCountP. + + *colorFinderP is a color finder based on 'colormap' -- it tells us what + index of 'colormap' corresponds to a certain color. +-----------------------------------------------------------------------------*/ + unsigned int col; + unsigned int missingCount; + + /* The following modify tuplerow, to make it consistent with + *outpamP instead of *inpamP. + */ + assert(outpamP->allocation_depth >= inpamP->depth); + + pnm_scaletuplerow(inpamP, outrow, inrow, outpamP->maxval); + + adjustDepthRow(outrow, outpamP->width, depthAdjustment); + + missingCount = 0; /* initial value */ + + for (col = 0; col < outpamP->width; ++col) { + bool missing; + mapTuple(outpamP, outrow[col], defaultColor, + colormap, colorFinderP, + colorhash, usehashP, outrow[col], &missing); + + if (missing) + ++missingCount; + } + + *missingCountP = missingCount; +} + - Use and update the Floyd-Steinberg state *fserrP. + +static void +convertRowDither(struct pam * const inpamP, + struct pam * const outpamP, + tuple const inrow[], + depthAdjustment const depthAdjustment, + tupletable const colormap, + struct colormapFinder * const colorFinderP, + tuplehash const colorhash, + bool * const usehashP, + tuple const defaultColor, + struct fserr * const fserrP, + tuple outrow[], + unsigned int * const missingCountP) { +/*---------------------------------------------------------------------------- + Like convertRow(), compute outrow[] from inrow[], replacing each pixel with + the new colors. Do a Floyd-Steinberg dither, using and updating the error + accumulator *fserrP. Return the number of pixels that were not matched in the color map as *missingCountP. @@ -638,79 +850,136 @@ convertRow(struct pam * const pamP, *colorFinderP is a color finder based on 'colormap' -- it tells us what index of 'colormap' corresponds to a certain color. -----------------------------------------------------------------------------*/ - int col; - int limitcol; - /* The column at which to stop processing the row. If we're scanning - forwards, this is the rightmost column. If we're scanning - backward, this is the leftmost column. + tuple const ditheredTuple = pnm_allocpamtuple(inpamP); + /* The input tuple we're converting, adjusted by the dither */ + tuple const normTuple = pnm_allocpamtuple(outpamP); + /* Same as above, normalized to the maxval of the output file / + colormap. */ - - if (floyd) - floydInitRow(pamP, fserrP); + unsigned int missingCount; + int col; - *missingCountP = 0; /* initial value */ + floydInitRow(inpamP, fserrP); + + missingCount = 0; /* initial value */ - if ((!floyd) || fserrP->fsForward) { - col = 0; - limitcol = pamP->width; - } else { - col = pamP->width - 1; - limitcol = -1; + for (col = fserrP->begCol; col != fserrP->endCol; col += fserrP->step) { + bool missing; + + floydAdjustColor(inpamP, inrow[col], ditheredTuple, fserrP, col); + + /* Convert tuple to the form of those in the colormap */ + assert(outpamP->allocation_depth >= inpamP->depth); + pnm_scaletuple(inpamP, normTuple, ditheredTuple, outpamP->maxval); + adjustDepthTuple(normTuple, depthAdjustment); + + mapTuple(outpamP, normTuple, defaultColor, + colormap, colorFinderP, + colorhash, usehashP, outrow[col], &missing); + + if (missing) + ++missingCount; + + /* Convert tuple back to the form of the input, where dithering + takes place. + */ + pnm_scaletuple(outpamP, normTuple, outrow[col], inpamP->maxval); + inverseAdjustDepthTuple(normTuple, depthAdjustment); + + floydPropagateErr(inpamP, fserrP, col, inrow[col], normTuple); } - do { - int colormapIndex; - /* Index into the colormap of the replacement color, or -1 if - there is no usable color in the color map. - */ - if (floyd) - floydAdjustColor(pamP, tuplerow[col], fserrP, col); + floydSwitchDir(inpamP, fserrP); - lookupThroughHash(pamP, tuplerow[col], - !!defaultColor, colorFinderP, - colorhash, &colormapIndex, usehashP); - if (floyd) - floydPropagateErr(pamP, fserrP, col, tuplerow[col], - colormap[colormapIndex]->tuple); + pnm_freepamtuple(normTuple); + pnm_freepamtuple(ditheredTuple); - if (colormapIndex == -1) { - ++*missingCountP; + *missingCountP = missingCount; +} - assert(defaultColor); // Otherwise, lookup would have succeeded - pnm_assigntuple(pamP, tuplerow[col], defaultColor); - } else - pnm_assigntuple(pamP, tuplerow[col], - colormap[colormapIndex]->tuple); - if (floyd && !fserrP->fsForward) - --col; - else - ++col; - } while (col != limitcol); +static void +convertRow(struct pam * const inpamP, + struct pam * const outpamP, + tuple inrow[], + depthAdjustment depthAdjustment, + tupletable const colormap, + struct colormapFinder * const colorFinderP, + tuplehash const colorhash, + bool * const usehashP, + bool const floyd, + tuple const defaultColor, + struct fserr * const fserrP, + tuple outrow[], + unsigned int * const missingCountP) { +/*---------------------------------------------------------------------------- + Replace the colors in row tuplerow[] (described by *inpamP) with the + new colors and convert so it is described by *outpamP. + + Use and update the Floyd-Steinberg state *fserrP. + + Return the number of pixels that were not matched in the color map as + *missingCountP. + + *colorFinderP is a color finder based on 'colormap' -- it tells us what + index of 'colormap' corresponds to a certain color. + outrow[] doubles as a work space, so we require it to have an allocation + depth at least as great as that of inrow[]. +-----------------------------------------------------------------------------*/ + /* The following both consults and adds to 'colorhash' and + its associated '*usehashP'. It modifies 'tuplerow' too. + */ if (floyd) - floydSwitchDir(pamP, fserrP); + convertRowDither(inpamP, outpamP, inrow, + depthAdjustment, colormap, colorFinderP, colorhash, + usehashP, defaultColor, + fserrP, outrow, missingCountP); + else + convertRowStraight(inpamP, outpamP, inrow, + depthAdjustment, colormap, colorFinderP, colorhash, + usehashP, defaultColor, + outrow, missingCountP); } static void -copyRaster(struct pam * const inpamP, - struct pam * const outpamP, - tupletable const colormap, - unsigned int const colormapSize, - bool const floyd, - tuple const defaultColor, - unsigned int * const missingCountP) { +copyRaster(struct pam * const inpamP, + struct pam * const outpamP, + tupletable const colormap, + unsigned int const colormapSize, + bool const floyd, + bool const randomize, + tuple const defaultColor, + unsigned int * const missingCountP) { tuplehash const colorhash = pnm_createtuplehash(); + + tuple * inrow; + tuple * outrow; + struct pam workpam; + /* This is for work space we use for building up the output + pixels. To save time and memory, we modify them in place in a + buffer, which ultimately holds the output pixels. This pam + structure is thus the same as the *outpamP, but with a tuple + allocation depth large enough to handle intermediate results. + */ + depthAdjustment depthAdjustment; struct colormapFinder * colorFinderP; bool usehash; struct fserr fserr; - tuple * tuplerow = pnm_allocpamrow(inpamP); int row; + workpam = *outpamP; + workpam.allocation_depth = MAX(workpam.depth, inpamP->depth); + workpam.size = sizeof(workpam); + workpam.len = PAM_STRUCT_SIZE(allocation_depth); + + inrow = pnm_allocpamrow(inpamP); + outrow = pnm_allocpamrow(&workpam); + if (outpamP->maxval != inpamP->maxval && defaultColor) pm_error("The maxval of the colormap (%u) is not equal to the " "maxval of the input image (%u). This is allowable only " @@ -718,40 +987,34 @@ copyRaster(struct pam * const inpamP, "specify -firstisdefault or -missingcolor)", (unsigned int)outpamP->maxval, (unsigned int)inpamP->maxval); + selectDepthAdjustment(inpamP, outpamP->depth, &depthAdjustment); + usehash = TRUE; createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP); if (floyd) - initFserr(inpamP, &fserr); + initFserr(inpamP, &fserr, randomize); *missingCountP = 0; /* initial value */ for (row = 0; row < inpamP->height; ++row) { unsigned int missingCount; - pnm_readpamrow(inpamP, tuplerow); + pnm_readpamrow(inpamP, inrow); - /* The following modify tuplerow, to make it consistent with - *outpamP instead of *inpamP. - */ - assert(inpamP->allocation_depth >= outpamP->depth); - pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval); - adjustDepth(inpamP, tuplerow, outpamP->depth); - - /* The following both consults and adds to 'colorhash' and - its associated 'usehash'. It modifies 'tuplerow' too. - */ - convertRow(outpamP, tuplerow, colormap, colorFinderP, colorhash, - &usehash, - floyd, defaultColor, &fserr, &missingCount); + convertRow(inpamP, &workpam, inrow, + depthAdjustment, colormap, colorFinderP, colorhash, + &usehash, floyd, defaultColor, + &fserr, outrow, &missingCount); *missingCountP += missingCount; - pnm_writepamrow(outpamP, tuplerow); + pnm_writepamrow(outpamP, outrow); } destroyColormapFinder(colorFinderP); - pnm_freepamrow(tuplerow); + pnm_freepamrow(inrow); + pnm_freepamrow(outrow); pnm_destroytuplehash(colorhash); } @@ -763,6 +1026,7 @@ remap(FILE * const ifP, tupletable const colormap, unsigned int const colormapSize, bool const floyd, + bool const randomize, tuple const defaultColor, bool const verbose) { /*---------------------------------------------------------------------------- @@ -801,7 +1065,7 @@ remap(FILE * const ifP, pnm_setminallocationdepth(&inpam, outpam.depth); copyRaster(&inpam, &outpam, colormap, colormapSize, floyd, - defaultColor, &missingCount); + randomize, defaultColor, &missingCount); if (verbose) pm_message("%u pixels not matched in color map", missingCount); @@ -920,7 +1184,7 @@ main(int argc, char * argv[] ) { } remap(ifP, &outpamCommon, colormap, colormapSize, - cmdline.floyd, defaultColor, + cmdline.floyd, !cmdline.norandom, defaultColor, cmdline.verbose); pnm_freepamtuple(firstColor); |