static void determineBackgroundColor(struct pam * const pamP, bool const verbose, tuple * const bgColorP) { /*---------------------------------------------------------------------------- Determine what color is the background color of the image in the file represented by *pamP. Expect the file to be positioned to the start of the raster, and leave it positioned arbitrarily. -----------------------------------------------------------------------------*/ unsigned int row; tuple * tuplerow; tuple ul, ur, ll, lr; /* Color of upper left, upper right, lower left, lower right */ tuplerow = pnm_allocpamrow(pamP); ul = pnm_allocpamtuple(pamP); ur = pnm_allocpamtuple(pamP); ll = pnm_allocpamtuple(pamP); lr = pnm_allocpamtuple(pamP); pnm_readpamrow(pamP, tuplerow); pnm_assigntuple(pamP, ul, tuplerow[0]); pnm_assigntuple(pamP, ur, tuplerow[pamP->width-1]); for (row = 1; row < pamP->height; ++row) pnm_readpamrow(pamP, tuplerow); pnm_assigntuple(pamP, ll, tuplerow[0]); pnm_assigntuple(pamP, lr, tuplerow[pamP->width-1]); selectBackground(pamP, ul, ur, ll, lr, bgColorP); if (verbose) { int const hexokTrue = 1; const char * const colorname = pnm_colorname(pamP, *bgColorP, hexokTrue); pm_message("Background color is %s", colorname); strfree(colorname); } pnm_freepamtuple(lr); pnm_freepamtuple(ll); pnm_freepamtuple(ur); pnm_freepamtuple(ul); pnm_freepamrow(tuplerow); }
static void getSpecifiedMissingColor(struct pam * const pamP, const char * const colorName, tuple * const specColorP) { tuple specColor; specColor = pnm_allocpamtuple(pamP); if (colorName) { pixel const color = ppm_parsecolor(colorName, pamP->maxval); if (pamP->depth == 3) { specColor[PAM_RED_PLANE] = PPM_GETR(color); specColor[PAM_GRN_PLANE] = PPM_GETG(color); specColor[PAM_BLU_PLANE] = PPM_GETB(color); } else if (pamP->depth == 1) { specColor[0] = ppm_luminosity(color); } else { pm_error("You may not use -missing with a colormap that is not " "of depth 1 or 3. Yours has depth %u", pamP->depth); } } *specColorP = specColor; }
static void processMapFile(const char * const mapFileName, struct pam * const outpamCommonP, tupletable * const colormapP, unsigned int * const colormapSizeP, tuple * const firstColorP) { /*---------------------------------------------------------------------------- Read a color map from the file named 'mapFileName'. 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. Also determine the first color (top left) in the map file and return that as *firstColorP. -----------------------------------------------------------------------------*/ FILE * mapfile; struct pam mappam; tuple ** maptuples; tuple firstColor; mapfile = pm_openr(mapFileName); maptuples = pnm_readpam(mapfile, &mappam, PAM_STRUCT_SIZE(tuple_type)); pm_close(mapfile); computeColorMapFromMap(&mappam, maptuples, colormapP, colormapSizeP); firstColor = pnm_allocpamtuple(&mappam); pnm_assigntuple(&mappam, firstColor, maptuples[0][0]); *firstColorP = firstColor; pnm_freepamarray(maptuples, &mappam); *outpamCommonP = mappam; outpamCommonP->file = stdout; }
static void computehashrecoverable(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, sample const newMaxval, unsigned int * const sizeP, tuplehash * const tuplefreqhashP, tuple ** const rowbufferP, tuple * const colorP) { /*---------------------------------------------------------------------------- This is computetuplefreqhash(), only it leaves a trail so that if it happens to longjmp out because of a failed memory allocation, the setjmp'er can cleanup whatever it had done so far. -----------------------------------------------------------------------------*/ unsigned int row; struct pam freqPam; bool full; freqPam = *pamP; freqPam.maxval = newMaxval; *tuplefreqhashP = pnm_createtuplehash(); *sizeP = 0; /* initial value */ *rowbufferP = pnm_allocpamrow(pamP); *colorP = pnm_allocpamtuple(&freqPam); full = FALSE; /* initial value */ /* Go through the entire raster, building a hash table of tuple values. */ for (row = 0; row < pamP->height && !full; ++row) { int col; const tuple * tuplerow; /* The row of tuples we are processing */ if (tupleArray) tuplerow = tupleArray[row]; else { pnm_readpamrow(pamP, *rowbufferP); tuplerow = *rowbufferP; } for (col = 0; col < pamP->width && !full; ++col) { pnm_scaletuple(pamP, *colorP, tuplerow[col], freqPam.maxval); addColorOccurrenceToHash( *colorP, *tuplefreqhashP, &freqPam, maxsize, sizeP, &full); } } pnm_freepamtuple(*colorP); *colorP = NULL; pnm_freepamrow(*rowbufferP); *rowbufferP = NULL; if (full) { pnm_destroytuplehash(*tuplefreqhashP); *tuplefreqhashP = NULL; } }
static void createWhiteTuple(const struct pam * const pamP, tuple * const whiteTupleP) { /*---------------------------------------------------------------------------- Create a "white" tuple. By that we mean a tuple all of whose elements are zero. If it's an RGB, grayscale, or b&w pixel, that means it's black. -----------------------------------------------------------------------------*/ tuple whiteTuple; unsigned int plane; whiteTuple = pnm_allocpamtuple(pamP); for (plane = 0; plane < pamP->depth; ++plane) whiteTuple[plane] = pamP->maxval; *whiteTupleP = whiteTuple; }
static void convertRowPsFilter(struct pam * const pamP, tuple * const tuplerow, struct bmepsoe * const bmepsoeP) { unsigned int const psMaxval = 255; unsigned int col; tuple scaledTuple; scaledTuple = pnm_allocpamtuple(pamP); for (col = 0; col < pamP->width; ++col) { unsigned int plane; pnm_scaletuple(pamP, scaledTuple, tuplerow[col], psMaxval); for (plane = 0; plane < pamP->depth; ++plane) outputBmepsSample(bmepsoeP, scaledTuple[plane]); } pnm_freepamtuple(scaledTuple); }
static void putMapEntry(struct pam * const pamP, tuple const value, int const size) { if (size == 15 || size == 16) { /* 5 bits each of red, green, and blue. Watch for byte order */ tuple const tuple31 = pnm_allocpamtuple(pamP); pnm_scaletuple(pamP, tuple31, value, 31); { int const mapentry = tuple31[PAM_BLU_PLANE] << 0 | tuple31[PAM_GRN_PLANE] << 5 | tuple31[PAM_RED_PLANE] << 10; putchar(mapentry % 256); putchar(mapentry / 256); } pnm_freepamtuple(tuple31); } else if (size == 8) putchar(pnm_scalesample(value[0], pamP->maxval, TGA_MAXVAL)); else { /* Must be 24 or 32 */ putchar(pnm_scalesample(value[PAM_BLU_PLANE], pamP->maxval, TGA_MAXVAL)); putchar(pnm_scalesample(value[PAM_GRN_PLANE], pamP->maxval, TGA_MAXVAL)); putchar(pnm_scalesample(value[PAM_RED_PLANE], pamP->maxval, TGA_MAXVAL)); if (size == 32) putchar(pnm_scalesample(value[PAM_TRN_PLANE], pamP->maxval, TGA_MAXVAL)); } }
static void selectBackground(struct pam * const pamP, tuple const ul, tuple const ur, tuple const lr, tuple const ll, tuple * const bgColorP) { tuple bg; /* Reference to one of ul, ur, ll, lr */ if (pnm_tupleequal(pamP, ul, ur) && (pnm_tupleequal(pamP, ul, ll) || pnm_tupleequal(pamP, ul, lr))) bg = ul; else if (pnm_tupleequal(pamP, ll, lr) && pnm_tupleequal(pamP, lr, ul)) bg = ll; else { /* No 3 corners are same color; look for 2 corners */ if (pnm_tupleequal(pamP, ul, ur)) /* top edge */ bg = ul; else if (pnm_tupleequal(pamP, ul, ll)) /* left edge */ bg = ul; else if (pnm_tupleequal(pamP, ur, lr)) /* right edge */ bg = ur; else if (pnm_tupleequal(pamP, ll, lr)) /* bottom edge */ bg = ll; else { /* No two corners are same color; just use upper left corner */ bg = ul; } } *bgColorP = pnm_allocpamtuple(pamP); pnm_assigntuple(pamP, *bgColorP, bg); }
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. *colorFinderP is a color finder based on 'colormap' -- it tells us what index of 'colormap' corresponds to a certain color. -----------------------------------------------------------------------------*/ 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. */ unsigned int missingCount; int col; floydInitRow(inpamP, fserrP); missingCount = 0; /* initial value */ 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); } floydSwitchDir(inpamP, fserrP); pnm_freepamtuple(normTuple); pnm_freepamtuple(ditheredTuple); *missingCountP = missingCount; }
static void createRowCutter(const struct pam * const inpamP, const struct pam * const outpamP, int const leftcol, int const rightcol, struct rowCutter ** const rowCutterPP) { struct rowCutter * rowCutterP; tuple * inputPointers; tuple * outputPointers; tuple * copyTuples; tuple blackTuple; tuple discardTuple; int col; assert(inpamP->depth >= outpamP->depth); /* Entry condition. If this weren't true, we could not simply treat an input tuple as an output tuple. */ copyTuples = pnm_allocpamrow(outpamP); discardTuple = pnm_allocpamtuple(inpamP); pnm_createBlackTuple(outpamP, &blackTuple); MALLOCARRAY_NOFAIL(inputPointers, inpamP->width); MALLOCARRAY_NOFAIL(outputPointers, outpamP->width); /* Put in left padding */ for (col = leftcol; col < 0 && col-leftcol < outpamP->width; ++col) outputPointers[col-leftcol] = blackTuple; /* Put in extracted columns */ for (col = MAX(leftcol, 0); col <= MIN(rightcol, inpamP->width-1); ++col) { int const outcol = col - leftcol; inputPointers[col] = outputPointers[outcol] = copyTuples[outcol]; } /* Put in right padding */ for (col = MIN(rightcol, inpamP->width-1) + 1; col <= rightcol; ++col) { if (col - leftcol >= 0) { outputPointers[col-leftcol] = blackTuple; } } /* Direct input pixels that are getting cut off to the discard tuple */ for (col = 0; col < MIN(leftcol, inpamP->width); ++col) inputPointers[col] = discardTuple; for (col = MAX(0, rightcol + 1); col < inpamP->width; ++col) inputPointers[col] = discardTuple; MALLOCVAR_NOFAIL(rowCutterP); rowCutterP->inputWidth = inpamP->width; rowCutterP->outputWidth = outpamP->width; rowCutterP->inputPointers = inputPointers; rowCutterP->outputPointers = outputPointers; rowCutterP->copyTuples = copyTuples; rowCutterP->discardTuple = discardTuple; rowCutterP->blackTuple = blackTuple; *rowCutterPP = rowCutterP; }