static void writeOutput(const struct pam * const inpamP, const unsigned char * const * const pi) { tuple black, white; tuple * outputTuplerow; /* This is not a normal tuplerow; it is just pointers to either 'black' or 'white' */ unsigned int row; struct pam outpam; initOutpam(inpamP, &outpam); allocateOutputPointerRow(outpam.width, &outputTuplerow); pnm_createBlackTuple(&outpam, &black); createWhiteTuple(&outpam, &white); pnm_writepaminit(&outpam); for (row = 0; row < outpam.height; ++row) { unsigned int col; for (col = 0; col < outpam.width; ++col) outputTuplerow[col] = pi[row][col] == PT_BG ? white : black; pnm_writepamrow(&outpam, outputTuplerow); } pnm_freepamtuple(white); pnm_freepamtuple(black); free(outputTuplerow); }
static void freeCmdline(struct cmdlineInfo const cmdline) { pnm_freepamtuple(cmdline.colorTopLeft); pnm_freepamtuple(cmdline.colorTopRight); pnm_freepamtuple(cmdline.colorBottomLeft); pnm_freepamtuple(cmdline.colorBottomRight); }
static void destroyRowCutter(struct rowCutter * const rowCutterP) { pnm_freepamrow(rowCutterP->copyTuples); pnm_freepamtuple(rowCutterP->blackTuple); pnm_freepamtuple(rowCutterP->discardTuple); free(rowCutterP->inputPointers); free(rowCutterP->outputPointers); free(rowCutterP); }
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 writeBlackRows(const struct pam * const outpamP, int const rows) { /*---------------------------------------------------------------------------- Write out 'rows' rows of black tuples of the image described by *outpamP. Unless our input image is PBM, PGM, or PPM, or PAM equivalent, we don't really know what "black" means, so this is just something arbitrary in that case. -----------------------------------------------------------------------------*/ tuple blackTuple; tuple * blackRow; int col; pnm_createBlackTuple(outpamP, &blackTuple); MALLOCARRAY_NOFAIL(blackRow, outpamP->width); for (col = 0; col < outpamP->width; ++col) blackRow[col] = blackTuple; pnm_writepamrowmult(outpamP, blackRow, rows); free(blackRow); pnm_freepamtuple(blackTuple); }
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; } }
int main(int argc, char *argv[]) { FILE * const rlefile = stdout; struct cmdlineInfo cmdline; FILE *ifP; /* Input (Netpbm) file */ struct pam pam; /* Description of the image */ tuple ** tupleArray; /* The image raster */ tupletable colormap; /* List of all of the colors used */ unsigned int numColors; /* Number of unique colors in the color map */ tuplehash colorhash; /* Mapping from color to index into colormap[] */ tuple transcolor; /* Color that should be considered transparent */ pnm_init (&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); tupleArray = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type)); transcolor = pnm_parsecolor(cmdline.transparent, pam.maxval); computeColorMap(&pam, tupleArray, &numColors, &colormap, &colorhash, cmdline.showcolormap); makeDjvurleHeader(rlefile, &pam, numColors, colormap); /* Write the raster */ { unsigned int row; for (row = 0; row < pam.height; ++row) writeDjvurleRow(rlefile, &pam, tupleArray[row], colorhash, transcolor); } /* Clean up */ pnm_freepamarray(tupleArray, &pam); pnm_freetupletable(&pam, colormap); pnm_destroytuplehash(colorhash); pnm_freepamtuple(transcolor); pm_close(ifP); return 0; }
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)); } }
int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; struct pam inpam; FILE * ifP; pm_filepos rasterpos; tuple backgroundColor; const unsigned char * const * pi; pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr_seekable(cmdline.inputFileName); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); pm_tell2(ifP, &rasterpos, sizeof(rasterpos)); determineBackgroundColor(&inpam, cmdline.verbose, &backgroundColor); pm_seek2(ifP, &rasterpos, sizeof(rasterpos)); findBackgroundPixels(&inpam, backgroundColor, cmdline.verbose, &pi); writeOutput(&inpam, pi); destroyPi(pi, inpam.height); pm_close(ifP); pnm_freepamtuple(backgroundColor); return 0; }
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; }
int main(int argc, const char * argv[] ) { struct CmdlineInfo cmdline; FILE * ifP; struct pam outpamCommon; /* Describes the output images. Width and height fields are not meaningful, because different output images might have different dimensions. The rest of the information is common across all output images. */ tupletable colormap; unsigned int colormapSize; 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 specify any. */ tuple firstColor; /* A tuple of the first color present in the map file */ tuple defaultColor; /* The color to which we will map an input color that is not in the colormap. NULL if we are not to map such a color to a particular color (i.e. we'll choose an approximate match from the map). */ pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); processMapFile(cmdline.mapFilespec, &outpamCommon, &colormap, &colormapSize, &firstColor); getSpecifiedMissingColor(&outpamCommon, cmdline.missingcolor, &specColor); switch (cmdline.missingMethod) { case MISSING_CLOSE: defaultColor = NULL; break; case MISSING_FIRST: defaultColor = firstColor; break; case MISSING_SPECIFIED: defaultColor = specColor; break; } remap(ifP, &outpamCommon, colormap, colormapSize, cmdline.floyd, !cmdline.norandom, defaultColor, cmdline.verbose); pnm_freepamtuple(firstColor); pnm_freepamtuple(specColor); pm_close(stdout); pm_close(ifP); return 0; }
static tuplehash computetuplefreqhash(struct pam * const pamP, tuple ** const tupleArray, unsigned int const maxsize, sample const newMaxval, unsigned int * const sizeP) { /*---------------------------------------------------------------------------- Compute a tuple frequency hash from a PAM. This is a hash that gives you the number of times a given tuple value occurs in the PAM. You can supply the input PAM in one of two ways: 1) a two-dimensional array of tuples tupleArray[][]; In this case, 'tupleArray' is non-NULL. 2) an open PAM file, positioned to the raster. In this case, 'tupleArray' is NULL. *pamP contains the file descriptor. We return with the file still open and its position undefined. In either case, *pamP contains parameters of the tuple array. Return the number of unique tuple values found as *sizeP. However, if the number of unique tuple values is greater than 'maxsize', return a null return value and *sizeP undefined. The tuple values that index the hash are scaled to a new maxval of 'newMaxval'. E.g. if the input has maxval 100 and 'newMaxval' is 50, and a particular tuple has sample value 50, it would be counted as sample value 25 in the hash. -----------------------------------------------------------------------------*/ tuplehash tuplefreqhash; tuple * rowbuffer; /* malloc'ed */ /* Buffer for a row read from the input file; undefined (but still allocated) if input is not from a file. */ tuple color; /* The color currently being added, scaled to the new maxval */ jmp_buf jmpbuf; jmp_buf * origJmpbufP; /* Initialize to "none" for purposes of error recovery */ tuplefreqhash = NULL; rowbuffer = NULL; color = NULL; if (setjmp(jmpbuf) == 0) { pm_setjmpbufsave(&jmpbuf, &origJmpbufP); computehashrecoverable(pamP, tupleArray, maxsize, newMaxval, sizeP, &tuplefreqhash, &rowbuffer, &color); pm_setjmpbuf(origJmpbufP); } else { if (color) pnm_freepamtuple(color); if (rowbuffer) pnm_freepamrow(rowbuffer); if (tuplefreqhash) pnm_destroytuplehash(tuplefreqhash); pm_longjmp(); } return tuplefreqhash; }