static void makeImageRows(const struct pam * const inPamP, outGenerator * const outputGeneratorP, double const depthOfField, double const eyesep, unsigned int const dpi, bool const crossEyed, bool const makeMask) { tuple * inRow; /* One row of pixels read from the height-map file */ tuple * outRow; /* One row of pixels to write to the height-map file */ int * same; /* Array: same[N] is the column number of a pixel to the right forced to have the same color as the one in column N */ int row; /* Current row in the input and output files */ inRow = pnm_allocpamrow(inPamP); outRow = pnm_allocpamrow(&outputGeneratorP->pam); MALLOCARRAY(same, inPamP->width); if (same == NULL) pm_error("Unable to allocate space for \"same\" array."); /* See the paper cited above for code comments. All I (Scott) did was * transcribe the code and make minimal changes for Netpbm. And some * style changes by Bryan to match Netpbm style. */ for (row = 0; row < inPamP->height; ++row) { pnm_readpamrow(inPamP, inRow); if (crossEyed) /* Invert heights for cross-eyed (as opposed to wall-eyed) people. */ invertHeightRow(inPamP, inRow); /* Determine color constraints. */ makeStereoRow(inPamP, inRow, same, depthOfField, eyesep, dpi); if (makeMask) makeMaskRow(&outputGeneratorP->pam, same, outRow); else makeImageRow(outputGeneratorP, row, same, outRow); /* Write the resulting row. */ pnm_writepamrow(&outputGeneratorP->pam, outRow); } free(same); pnm_freepamrow(outRow); pnm_freepamrow(inRow); }
static void doUnlookup(struct pam * const inpamP, tuplehash const lookupHash, sample const maxIndex, FILE * const ofP) { struct pam outpam; unsigned int row; tuple * inrow; tuple * outrow; inrow = pnm_allocpamrow(inpamP); outpam.size = sizeof(outpam); outpam.len = PAM_STRUCT_SIZE(tuple_type); outpam.file = ofP; outpam.format = PAM_FORMAT; outpam.height = inpamP->height; outpam.width = inpamP->width; outpam.depth = 1; outpam.maxval = maxIndex + 1; /* +1 for missing color */ strcpy(outpam.tuple_type, "INDEX"); pnm_writepaminit(&outpam); outrow = pnm_allocpamrow(&outpam); for (row = 0; row < inpamP->height; ++row) { unsigned int col; pnm_readpamrow(inpamP, inrow); for (col = 0; col < inpamP->width; ++col) { int found; int index; pnm_lookuptuple(inpamP, lookupHash, inrow[col], &found, &index); if (found) { assert(index <= outpam.maxval); outrow[col][0] = index; } else outrow[col][0] = maxIndex + 1; } pnm_writepamrow(&outpam, outrow); } pnm_freepamrow(outrow); pnm_freepamrow(inrow); }
static void extractOneImage(FILE * const infileP, FILE * const outfileP) { struct pam inpam; struct pam outpam; enum pm_check_code checkRetval; unsigned int row; tuple * tuplerow; pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type)); pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval); outpam = inpam; outpam.file = outfileP; pnm_writepaminit(&outpam); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); pnm_writepamrow(&outpam, tuplerow); } pnm_freepamrow(tuplerow); }
static void thresholdSimple(struct pam * const inpamP, struct pam * const outpamP, float const threshold) { tuplen * inrow; /* normalized input row */ tuple * outrow; /* raw output row */ unsigned int row; /* number of the current row */ inrow = pnm_allocpamrown(inpamP); outrow = pnm_allocpamrow(outpamP); /* do the simple thresholding */ for (row = 0; row < inpamP->height; ++row) { unsigned int col; pnm_readpamrown(inpamP, inrow); for (col = 0; col < inpamP->width; ++col) { thresholdPixel(outpamP, inrow[col], outrow[col], threshold); } pnm_writepamrow(outpamP, outrow); } pnm_freepamrow(inrow); pnm_freepamrow(outrow); }
static void initPi(unsigned char ** const pi, const struct pam * const pamP, tuple const backgroundColor) { /*---------------------------------------------------------------------------- Set the initial info about every pixel in pi[][]. Read through the image in the file described by *inpamP and set each pixel which is not of background color, and therefore obviously foreground, to type PT_FG. Set every other pixel to PT_UNKNOWN. -----------------------------------------------------------------------------*/ tuple * tuplerow; unsigned int row; tuplerow = pnm_allocpamrow(pamP); for (row = 0; row < pamP->height; ++row) { unsigned int col; pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { pi[row][col] = pnm_tupleequal(pamP, tuplerow[col], backgroundColor) ? PT_UNKNOWN : PT_FG; } } pnm_freepamrow(tuplerow); }
static void addImageColorsToHash(struct pam * const pamP, tuplehash const tuplehash, unsigned int * const colorCountP) { tuple * tuplerow; unsigned int row; tuplerow = pnm_allocpamrow(pamP); for (row = 0; row < pamP->height; ++row) { unsigned int col; pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { int firstOccurrence; pnm_addtuplefreqoccurrence(pamP, tuplerow[col], tuplehash, &firstOccurrence); if (firstOccurrence) ++(*colorCountP); } } pnm_freepamrow(tuplerow); }
static void extractOneImage(FILE * const infileP, FILE * const outfileP) { /*---------------------------------------------------------------------------- Copy a complete image from input stream *infileP to output stream *outfileP. But if outfileP == NULL, just read the image and discard it. -----------------------------------------------------------------------------*/ struct pam inpam; struct pam outpam; enum pm_check_code checkRetval; unsigned int row; tuple * tuplerow; pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type)); pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval); outpam = inpam; outpam.file = outfileP; if (outfileP) pnm_writepaminit(&outpam); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); if (outfileP) pnm_writepamrow(&outpam, tuplerow); } pnm_freepamrow(tuplerow); }
static void analyzeDistribution(struct pam * const inpamP, bool const verbose, const unsigned int ** const histogramP, struct range * const rangeP) { /*---------------------------------------------------------------------------- Find the distribution of the sample values -- minimum, maximum, and how many of each value -- in input image *inpamP, whose file is positioned to the raster. Return the minimum and maximum as *rangeP and the frequency distribution as *histogramP, an array such that histogram[i] is the number of pixels that have sample value i. Assume the file is positioned to the raster upon entry and leave it positioned at the same place. -----------------------------------------------------------------------------*/ unsigned int row; tuple * inrow; tuplen * inrown; unsigned int * histogram; /* malloced array */ unsigned int i; pm_filepos rasterPos; /* Position in input file of the raster */ pm_tell2(inpamP->file, &rasterPos, sizeof(rasterPos)); inrow = pnm_allocpamrow(inpamP); inrown = pnm_allocpamrown(inpamP); MALLOCARRAY(histogram, inpamP->maxval+1); if (histogram == NULL) pm_error("Unable to allocate space for %lu-entry histogram", inpamP->maxval+1); /* Initialize histogram -- zero occurrences of everything */ for (i = 0; i <= inpamP->maxval; ++i) histogram[i] = 0; initRange(rangeP); for (row = 0; row < inpamP->height; ++row) { unsigned int col; pnm_readpamrow(inpamP, inrow); pnm_normalizeRow(inpamP, inrow, NULL, inrown); for (col = 0; col < inpamP->width; ++col) { ++histogram[inrow[col][0]]; addToRange(rangeP, inrown[col][0]); } } *histogramP = histogram; pnm_freepamrow(inrow); pnm_freepamrown(inrown); pm_seek2(inpamP->file, &rasterPos, sizeof(rasterPos)); if (verbose) pm_message("Pixel values range from %f to %f", rangeP->min, rangeP->max); }
static void writePam(struct pam * const outpamP, unsigned int const nfiles, const coord * const coords, const struct pam * const imgs) { tuple *tuplerow; int i; pnm_writepaminit(outpamP); tuplerow = pnm_allocpamrow(outpamP); for (i = 0; i < outpamP->height; ++i) { int j; for (j = 0; j < nfiles; ++j) { if (coords[j].y <= i && i < coords[j].y + imgs[j].height) { pnm_readpamrow(&imgs[j], &tuplerow[coords[j].x]); adjustDepth(tuplerow, &imgs[j], outpamP, coords[j]); adjustMaxval(tuplerow, &imgs[j], outpamP, coords[j]); } } pnm_writepamrow(outpamP, tuplerow); } pnm_freepamrow(tuplerow); }
static void wipeoutLr(FILE * const ifP, FILE * const ofP) { /* left-right we can read row-by-row */ struct pam inpam, outpam; tuple ** tuples; tuple * tuplerow; unsigned int row; pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); outpam = inpam; outpam.file = ofP; pnm_writepaminit(&outpam); tuplerow = pnm_allocpamrow(&inpam); for (row = 0; row < inpam.height; ++row) { pnm_readpamrow(&inpam, tuplerow); wipeRowByCol(inpam, tuples, tuplerow); pnm_writepamrow(&outpam, tuplerow); } pnm_freepamrow(tuplerow); }
int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; struct pam pam; tuple * tupleRow; tuple * leftEdge; tuple * rightEdge; unsigned int row; pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); pam.size = sizeof pam; pam.len = PAM_STRUCT_SIZE(tuple_type); pam.file = stdout; pam.plainformat = 0; pam.width = cmdline.cols; pam.height = cmdline.rows; pam.maxval = cmdline.maxval; pam.bytes_per_sample = pnm_bytespersample(pam.maxval); pam.format = PAM_FORMAT; if (isgray(&pam, cmdline.colorTopLeft) && isgray(&pam, cmdline.colorTopRight) && isgray(&pam, cmdline.colorBottomLeft) && isgray(&pam, cmdline.colorBottomRight)) { pam.depth = 1; strcpy(pam.tuple_type, PAM_PGM_TUPLETYPE); } else { pam.depth = 3; strcpy(pam.tuple_type, PAM_PPM_TUPLETYPE); } pnm_writepaminit(&pam); tupleRow = pnm_allocpamrow(&pam); leftEdge = createEdge(&pam, cmdline.colorTopLeft, cmdline.colorBottomLeft); rightEdge = createEdge(&pam, cmdline.colorTopRight, cmdline.colorBottomRight); /* interpolate each row between the left edge and the right edge */ for (row = 0; row < pam.height; ++row) { interpolate(&pam, tupleRow, leftEdge[row], rightEdge[row]); pnm_writepamrow(&pam, tupleRow); } pm_close(stdout); pnm_freepamrow(rightEdge); pnm_freepamrow(leftEdge); pnm_freepamrow(tupleRow); freeCmdline(cmdline); return 0; }
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 * ifP; tuple * tuplerow; /* Row from input image */ unsigned int row; struct cmdlineInfo cmdline; struct pam inpam; struct pam outpam; pnm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); tuplerow = pnm_allocpamrow(&inpam); outpam = inpam; /* Initial value -- most fields should be same */ outpam.file = stdout; if (inpam.height % 2 == 0) outpam.height = inpam.height / 2; else { if (cmdline.rowsToTake == ODD) outpam.height = inpam.height / 2; else outpam.height = inpam.height / 2 + 1; } pnm_writepaminit(&outpam); { unsigned int modulusToTake; /* The row number mod 2 of the rows that are supposed to go into the output. */ switch (cmdline.rowsToTake) { case EVEN: modulusToTake = 0; break; case ODD: modulusToTake = 1; break; default: pm_error("INTERNAL ERROR: invalid rowsToTake"); } /* Read input and write out rows extracted from it */ for (row = 0; row < inpam.height; row++) { pnm_readpamrow(&inpam, tuplerow); if (row % 2 == modulusToTake) pnm_writepamrow(&outpam, tuplerow); } } pnm_freepamrow(tuplerow); pm_close(inpam.file); pm_close(outpam.file); return 0; }
static void writeAnaglyph( FILE * const ofP, gray ** const grayArray, gray const maxGrayVal, int const eyeSep, int const swapEyes, struct pam * const pamP) { /*---------------------------------------------------------------------- Output an anaglyphic stereogram from the given grayscale array and eye-separation value. ------------------------------------------------------------------------*/ struct pam outPam; tuple * tuplerow; outPam.size = sizeof(struct pam); outPam.len = PAM_STRUCT_SIZE(tuple_type); outPam.file = ofP; outPam.format = PAM_FORMAT; outPam.plainformat = 0; outPam.height = pamP->height; outPam.width = pamP->width - eyeSep; /* Avoid color bands on the left/right edges. */ outPam.depth = 3; outPam.maxval = (sample) maxGrayVal; strcpy(outPam.tuple_type, PAM_PPM_TUPLETYPE); pnm_writepaminit( &outPam ); tuplerow = pnm_allocpamrow( &outPam ); if (swapEyes) { unsigned int row; for (row = 0; row < outPam.height; ++row) { unsigned int col; for (col = 0; col < outPam.width; ++col) { tuplerow[col][PAM_RED_PLANE] = grayArray[row][col+eyeSep]; tuplerow[col][PAM_GRN_PLANE] = grayArray[row][col]; tuplerow[col][PAM_BLU_PLANE] = grayArray[row][col]; } pnm_writepamrow( &outPam, tuplerow ); } } else { unsigned int row; for (row = 0; row < outPam.height; ++row) { unsigned int col; for (col = 0; col < outPam.width; ++col) { tuplerow[col][PAM_RED_PLANE] = grayArray[row][col]; tuplerow[col][PAM_GRN_PLANE] = grayArray[row][col+eyeSep]; tuplerow[col][PAM_BLU_PLANE] = grayArray[row][col+eyeSep]; } pnm_writepamrow( &outPam, tuplerow ); } } pnm_freepamrow( tuplerow ); }
static void convertToPamPnm(struct pam * const outpamP, jas_image_t * const jasperP, int const jasperCmptNo[]) { jas_matrix_t ** matrix; /* malloc'ed */ /* matrix[X] is the data for Plane X of the current row */ sample * jasperMaxval; unsigned int row; tuple * tuplerow; jas_seqent_t ** jasperRow; /* malloc'ed */ /* A row of a plane of the raster from the Jasper library This is an array of pointers into the 'matrix' data structures. */ bool singleMaxval; createMatrices(outpamP, &matrix); computeComponentMaxval(outpamP, jasperP, jasperCmptNo, &jasperMaxval, &singleMaxval); MALLOCARRAY(jasperRow, outpamP->depth); if (jasperRow == NULL) pm_error("Out of memory"); tuplerow = pnm_allocpamrow(outpamP); for (row = 0; row < outpamP->height; ++row) { unsigned int plane; for (plane = 0; plane < outpamP->depth; ++plane) { int rc; rc = jas_image_readcmpt(jasperP, jasperCmptNo[plane], 0, row, outpamP->width, 1, matrix[plane]); if (rc != 0) pm_error("jas_image_readcmpt() of row %u plane %u " "failed.", row, plane); jasperRow[plane] = jas_matrix_getref(matrix[plane], 0, 0); } if (singleMaxval) copyRowSingleMaxval(jasperRow, tuplerow, outpamP); else copyRowAnyMaxval(jasperRow, tuplerow, outpamP, jasperMaxval); pnm_writepamrow(outpamP, tuplerow); } pnm_freepamrow(tuplerow); destroyMatrices(outpamP, matrix); free(jasperRow); free(jasperMaxval); }
/* Draw a pair of guide boxes. */ static void drawguides(int const guidesize, const struct pam * const outPamP, double const eyesep, unsigned int const dpi, double const depthOfField) { int const far = separation(0, eyesep, dpi, depthOfField); /* Space between the two guide boxes. */ int const width = outPamP->width; /* Width of the output image */ tuple *outrow; /* One row of output data */ tuple blackTuple; int col; pnm_createBlackTuple(outPamP, &blackTuple); outrow = pnm_allocpamrow(outPamP); /* Leave some blank rows before the guides. */ makeWhiteRow(outPamP, outrow); writeRowCopies(outPamP, outrow, guidesize); /* Draw the guides. */ if ((width - far + guidesize)/2 < 0 || (width + far - guidesize)/2 >= width) pm_message("warning: the guide boxes are completely out of bounds " "at %d DPI", dpi); else if ((width - far - guidesize)/2 < 0 || (width + far + guidesize)/2 >= width) pm_message("warning: the guide boxes are partially out of bounds " "at %d DPI", dpi); for (col = (width - far - guidesize)/2; col < (width - far + guidesize)/2; ++col) if (col >= 0 && col < width) pnm_assigntuple(outPamP, outrow[col], blackTuple); for (col = (width + far - guidesize)/2; col < (width + far + guidesize)/2; ++col) if (col >= 0 && col < width) pnm_assigntuple(outPamP, outrow[col], blackTuple); writeRowCopies(outPamP,outrow, guidesize); /* Leave some blank rows after the guides. */ makeWhiteRow(outPamP, outrow); writeRowCopies(outPamP, outrow, guidesize); pnm_freerow(outrow); }
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); }
int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; FILE* ifP; struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PNM image */ pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); validateTupleType(inpam, cmdline.assume); outpam = inpam; outpam.file = stdout; if (inpam.depth < 3) { outpam.depth = 1; if (inpam.maxval == 1) outpam.format = PBM_FORMAT; else outpam.format = PGM_FORMAT; } else { outpam.depth = 3; outpam.format = PPM_FORMAT; } pnm_writepaminit(&outpam); { tuple *tuplerow; tuplerow = pnm_allocpamrow(&inpam); { int row; for (row = 0; row < inpam.height; row++) { pnm_readpamrow(&inpam, tuplerow); pnm_writepamrow(&outpam, tuplerow); } } pnm_freepamrow(tuplerow); } return 0; }
static void writeImgPam(IPDB * const pdbP, FILE * const ofP) { struct pam pam; tuple * tupleRow; unsigned int row; uint8_t * imgRow; MALLOCARRAY(imgRow, ipdb_width(pdbP)); pam.size = sizeof(pam); pam.len = PAM_STRUCT_SIZE(tuple_type); pam.file = ofP; pam.plainformat = 0; pam.width = ipdb_width(pdbP); pam.height = ipdb_height(pdbP); pam.depth = 1; pam.maxval = ipdb_type(pdbP) == IMG_MONO ? 1 : 255; pam.bytes_per_sample = pnm_bytespersample(pam.maxval); pam.format = PAM_FORMAT; strcpy(pam.tuple_type, ipdb_type(pdbP) == IMG_MONO ? PAM_PBM_TUPLETYPE : PAM_PGM_TUPLETYPE); pnm_writepaminit(&pam); tupleRow = pnm_allocpamrow(&pam); for (row = 0; row < pam.height; ++row) { unsigned int col; if (ipdb_type(pdbP) == IMG_MONO) mrow(pdbP, row, imgRow); else if (ipdb_type(pdbP) == IMG_GRAY) grow(pdbP, row, imgRow); else g16row(pdbP, row, imgRow); for (col = 0; col < pam.width; ++col) tupleRow[col][0] = imgRow[col]; pnm_writepamrow(&pam, tupleRow); } pnm_freepamrow(tupleRow); free(imgRow); }
static void ditherImage(struct pam * const inpamP, const scaler * const scalerP, unsigned int const dithPower, struct colorResolution const colorRes, struct pam * const outpamP, tuple *** const outTuplesP) { unsigned int const dithDim = 1 << dithPower; unsigned int const ditherMatrixArea = SQR(dithDim); unsigned int const modMask = (dithDim - 1); /* And this into N to compute N % dithDim cheaply, since we know (though the compiler doesn't) that dithDim is a power of 2 */ unsigned int ** const ditherMatrix = dithMatrix(dithPower); tuple * inrow; tuple ** outTuples; unsigned int row; struct pam ditherPam; /* Describes the tuples that ditherRow() sees */ assert(dithPower < sizeof(unsigned int) * 8); assert(UINT_MAX / dithDim >= dithDim); validateNoDitherOverflow(ditherMatrixArea, inpamP, colorRes); inrow = pnm_allocpamrow(inpamP); outTuples = pnm_allocpamarray(outpamP); /* We will modify the input to promote it to depth 3 */ ditherPam = *inpamP; ditherPam.depth = 3; for (row = 0; row < inpamP->height; ++row) { pnm_readpamrow(inpamP, inrow); pnm_makerowrgb(inpamP, inrow); ditherRow(&ditherPam, inrow, scalerP, ditherMatrix, ditherMatrixArea, colorRes, row, modMask, outpamP, outTuples[row]); } free(ditherMatrix); pnm_freepamrow(inrow); *outTuplesP = outTuples; }
static void load(const struct pam * const pamP, unsigned int const hstep, sample *** const pixelsP, unsigned int * const hsamplesP) { /*---------------------------------------------------------------------------- read file into memory, returning array of rows -----------------------------------------------------------------------------*/ unsigned int const hsamples = 1 + (pamP->width - 1) / hstep; /* use only this many cols */ unsigned int row; tuple * tuplerow; sample ** pixels; tuplerow = pnm_allocpamrow(pamP); MALLOCARRAY(pixels, pamP->height); if (pixels == NULL) pm_error("Unable to allocate array of %u pixel rows", pamP->height); if (pixels == NULL) pm_error("Unable to allocate array of %u rows", pamP->height); for (row = 0; row < pamP->height; ++row) { unsigned int i; unsigned int col; pnm_readpamrow(pamP, tuplerow); MALLOCARRAY(pixels[row], hsamples); if (pixels[row] == NULL) pm_error("Unable to allocate %u-sample array for Row %u", hsamples, row); /* save every hstep'th column */ for (i = col = 0; i < hsamples; ++i, col += hstep) pixels[row][i] = tuplerow[col][0]; } pnm_freepamrow(tuplerow); *hsamplesP = hsamples; *pixelsP = pixels; }
static void doOneImage(const char * const name, unsigned int const imageDoneCount, FILE * const fileP, bool const allimages, bool const justCount, bool const wantComments, bool * const eofP) { struct pam pam; const char * comments; enum pm_check_code checkRetval; pam.comment_p = &comments; pnm_readpaminit(fileP, &pam, PAM_STRUCT_SIZE(comment_p)); if (!justCount) { if (allimages) printf("%s:\tImage %d:\t", name, imageDoneCount); else printf("%s:\t", name); dumpHeader(pam); if (wantComments) dumpComments(comments); } strfree(comments); pnm_checkpam(&pam, PM_CHECK_BASIC, &checkRetval); if (allimages) { tuple * tuplerow; unsigned int row; tuplerow = pnm_allocpamrow(&pam); for (row = 0; row < pam.height; ++row) pnm_readpamrow(&pam, tuplerow); pnm_freepamrow(tuplerow); pnm_nextimage(fileP, eofP); } }
static tuple * createEdge(const struct pam * const pamP, tuple const topColor, tuple const bottomColor) { /*---------------------------------------------------------------------------- Create a left or right edge, interpolating from top to bottom. -----------------------------------------------------------------------------*/ struct pam interpPam; tuple * tupleRow; interpPam = *pamP; /* initial value */ interpPam.width = pamP->height; interpPam.height = 1; tupleRow = pnm_allocpamrow(&interpPam); interpolate(&interpPam, tupleRow, topColor, bottomColor); return tupleRow; }
static void initRandomColor(outGenerator * const outGenP, const struct pam * const inPamP, struct cmdlineInfo const cmdline) { struct randomState * stateP; outGenP->pam.format = PAM_FORMAT; outGenP->pam.plainformat = 0; switch (cmdline.outputType) { case OUTPUT_BW: strcpy(outGenP->pam.tuple_type, PAM_PBM_TUPLETYPE); outGenP->pam.maxval = 1; outGenP->pam.depth = 1; break; case OUTPUT_GRAYSCALE: strcpy(outGenP->pam.tuple_type, PAM_PGM_TUPLETYPE); outGenP->pam.maxval = cmdline.maxvalSpec ? cmdline.maxval : inPamP->maxval; outGenP->pam.depth = 1; break; case OUTPUT_COLOR: strcpy(outGenP->pam.tuple_type, PAM_PPM_TUPLETYPE); outGenP->pam.maxval = cmdline.maxvalSpec ? cmdline.maxval : inPamP->maxval; outGenP->pam.depth = 3; break; } MALLOCVAR_NOFAIL(stateP); stateP->currentRow = pnm_allocpamrow(&outGenP->pam); stateP->magnifypat = cmdline.magnifypat; stateP->prevy = (unsigned int)(-cmdline.magnifypat); outGenP->stateP = stateP; outGenP->getTuple = &randomColor; outGenP->terminateState = &termRandomColor; }
static void writePam(struct pam * const outpamP, unsigned int const imgCt, const Coord * const coords, const struct pam * const imgs) { /*---------------------------------------------------------------------------- Write the entire composite image. There are 'imgCt' source images, described by imgs[]. Their placement in the output is coords[]. Properties of the output image, including where to write it and its dimensions are *outpamP. -----------------------------------------------------------------------------*/ tuple * tuplerow; unsigned int row; /* Row number in the output image */ pnm_writepaminit(outpamP); tuplerow = pnm_allocpamrow(outpamP); for (row = 0; row < outpamP->height; ++row) { unsigned int imgIdx; makeRowBlack(outpamP, tuplerow); /* initial value */ for (imgIdx = 0; imgIdx < imgCt; ++imgIdx) { const Coord * const imgCoordP = &coords[imgIdx]; const struct pam * const imgPamP = &imgs[imgIdx]; if (imgCoordP->y <= row && row < imgCoordP->y + imgPamP->height) { pnm_readpamrow(imgPamP, &tuplerow[imgCoordP->x]); adjustDepth(tuplerow, imgPamP, outpamP, *imgCoordP); adjustMaxval(tuplerow, imgPamP, outpamP, *imgCoordP); } } pnm_writepamrow(outpamP, tuplerow); } pnm_freepamrow(tuplerow); }
static void writePam(FILE * const ofP, canvas * const canvasP) { unsigned int row; struct pam pam; tuple * tuplerow; pam.size = sizeof(pam); pam.len = PAM_STRUCT_SIZE(tuple_type); pam.file = ofP; pam.format = PAM_FORMAT; pam.plainformat = 0; pam.width = canvasP->width; pam.height = canvasP->height; pam.depth = 3; pam.maxval = OUTPUT_MAXVAL; strcpy(pam.tuple_type, PAM_PPM_TUPLETYPE); pnm_writepaminit(&pam); tuplerow = pnm_allocpamrow(&pam); for (row = 0; row < (unsigned)pam.height; ++row) { pixel * const pixelrow = canvasP->pixels[row]; unsigned int col; for (col = 0; col < (unsigned)pam.width; ++col) { pixel const thisPixel = pixelrow[col]; assert(pam.depth >= 3); tuplerow[col][PAM_RED_PLANE] = PPM_GETR(thisPixel); tuplerow[col][PAM_GRN_PLANE] = PPM_GETG(thisPixel); tuplerow[col][PAM_BLU_PLANE] = PPM_GETB(thisPixel); } pnm_writepamrow(&pam, tuplerow); } pnm_freepamrow(tuplerow); }
int main(int argc, char *argv[]) { FILE *ifP; struct cmdlineInfo cmdline; struct pam inpam, outpam; unsigned int row; tuple * inrow; tuple * outrow; tuple * prevrow; pnm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); outpam = inpam; outpam.file = stdout; outpam.format = PAM_FORMAT; strcpy(outpam.tuple_type, "hdiff"); pnm_writepaminit(&outpam); inrow = pnm_allocpamrow(&inpam); outrow = pnm_allocpamrow(&outpam); prevrow = pnm_allocpamrow(&inpam); pnm_setpamrow(&inpam, prevrow, 0); /* All arithmetic in this operation and in the reverse operation (to recover the image) is done modulus the maxval+1 (the hdiff PAM and the image have the same maxval) in order to produce legal PAM samples (which must be in the range 0..maxval). This might seem to throw away information, but it doesn't. Example: maxval is 99. Intensity goes from 90 in Row 0 to 10 in Row 1. The difference is -80. -80 mod 100 is 20, so 20 goes in the hdiff output. On reconstructing the image, the interpreter knows the "20" can't be +20, because that would create the sample 90 + 20 = 110, and violate maxval. So it must be -80. Modulus arithmetic by the interpreter effectively makes that decision. */ /* The bias is just to make it easier to look at the output visually. If you display the values as intensities, and your differences are all +/- half of maxval, you can see positive transitions as bright spots and negative transitions as dark spots. */ { unsigned int const bias = outpam.maxval/2; for (row = 0; row < inpam.height; ++row) { unsigned int col; pnm_readpamrow(&inpam, inrow); for (col = 0; col < inpam.width; ++col) { unsigned int plane; for (plane = 0; plane < inpam.depth; ++plane) { sample const sampleValue = inrow[col][plane]; int const difference = sampleValue - prevrow[col][plane]; outrow[col][plane] = (difference + bias) % (outpam.maxval+1); prevrow[col][plane] = sampleValue; } } pnm_writepamrow(&outpam, outrow); } } pnm_freepamrow(prevrow); pnm_freepamrow(outrow); pnm_freepamrow(inrow); exit(0); }
static void composite(int const originleft, int const origintop, struct pam * const underlayPamP, struct pam * const overlayPamP, struct pam * const alphaPamP, bool const invertAlpha, float const masterOpacity, struct pam * const composedPamP, bool const assumeLinear, bool const mixTransparency) { /*---------------------------------------------------------------------------- Overlay the overlay image in the array 'overlayImage', described by *overlayPamP, onto the underlying image from the input image file as described by *underlayPamP, output the composite to the image file as described by *composedPamP. Apply the overlay image with transparency described by the array 'alpha' and *alphaPamP. The underlying image is positioned after its header. 'originleft' and 'origintop' are the coordinates in the underlying image plane where the top left corner of the overlay image is to go. It is not necessarily inside the underlying image (in fact, may be negative). Only the part of the overlay that actually intersects the underlying image, if any, gets into the output. We assume that the span from the topmost row of the two images to the bottommost row is less than INT_MAX. -----------------------------------------------------------------------------*/ enum sampleScale const sampleScale = assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE; enum alphaMix const alphaMix = mixTransparency ? AM_OVERLAY : AM_KEEPUNDER; int underlayRow; /* NB may be negative */ int overlayRow; /* NB may be negative */ tuple * composedTuplerow; tuple * underlayTuplerow; struct pam adaptUnderlayPam; tuple * overlayTuplerow; struct pam adaptOverlayPam; tuplen * alphaTuplerown; composedTuplerow = pnm_allocpamrow(composedPamP); underlayTuplerow = pnm_allocpamrow(underlayPamP); overlayTuplerow = pnm_allocpamrow(overlayPamP); if (alphaPamP) alphaTuplerown = pnm_allocpamrown(alphaPamP); else alphaTuplerown = NULL; determineInputAdaptations(underlayPamP, overlayPamP, composedPamP, &adaptUnderlayPam, &adaptOverlayPam); pnm_writepaminit(composedPamP); assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */ for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop); underlayRow < MAX(underlayPamP->height, origintop + overlayPamP->height); ++underlayRow, ++overlayRow) { if (overlayRow >= 0 && overlayRow < overlayPamP->height) { pnm_readpamrow(overlayPamP, overlayTuplerow); adaptRowFormat(overlayPamP, &adaptOverlayPam, overlayTuplerow); if (alphaPamP) pnm_readpamrown(alphaPamP, alphaTuplerown); } if (underlayRow >= 0 && underlayRow < underlayPamP->height) { pnm_readpamrow(underlayPamP, underlayTuplerow); adaptRowFormat(underlayPamP, &adaptUnderlayPam, underlayTuplerow); if (underlayRow < origintop || underlayRow >= origintop + overlayPamP->height) { /* Overlay image does not touch this underlay row. */ pnm_writepamrow(composedPamP, underlayTuplerow); } else { composeRow(originleft, &adaptUnderlayPam, &adaptOverlayPam, invertAlpha, masterOpacity, composedPamP, sampleScale, alphaMix, underlayTuplerow, overlayTuplerow, alphaTuplerown, composedTuplerow); pnm_writepamrow(composedPamP, composedTuplerow); } } } pnm_freepamrow(composedTuplerow); pnm_freepamrow(underlayTuplerow); pnm_freepamrow(overlayTuplerow); if (alphaPamP) pnm_freepamrown(alphaTuplerown); }
static void 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; 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 " "if you are doing an approximate mapping (i.e. you don't " "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, randomize); *missingCountP = 0; /* initial value */ for (row = 0; row < inpamP->height; ++row) { unsigned int missingCount; pnm_readpamrow(inpamP, inrow); convertRow(inpamP, &workpam, inrow, depthAdjustment, colormap, colorFinderP, colorhash, &usehash, floyd, defaultColor, &fserr, outrow, &missingCount); *missingCountP += missingCount; pnm_writepamrow(outpamP, outrow); } destroyColormapFinder(colorFinderP); pnm_freepamrow(inrow); pnm_freepamrow(outrow); pnm_destroytuplehash(colorhash); }
static void writeScanLines(struct pam * const pamP, TIFF * const tif, tuplehash const cht, unsigned short const tiffMaxval, unsigned short const bitspersample, unsigned short const photometric, int const bytesperrow, int const fillorder) { /*---------------------------------------------------------------------------- Write out the raster for the input image described by 'pamP', whose file is positioned to the raster of the image. -----------------------------------------------------------------------------*/ tuple * tuplerow; /* malloc'ed */ unsigned char * buf; /* malloc'ed */ int row; /* The man page for TIFFWriteScanLine doesn't tell the format of it's 'buf' parameter, but here it is: Its format depends on the bits per pixel of the TIFF image. If it's 16, 'buf' is an array of short (16 bit) integers, one per raster column. If it's 8, 'buf' is an array of characters (8 bit integers), one per image column. If it's less than 8, it's an array of characters, each of which represents 1-8 raster columns, packed into it in the order specified by the TIFF image's fill order, with don't-care bits on the right such that each byte contains only whole pixels. In all cases, the array elements are in order left to right going from low array indices to high array indices. */ MALLOCARRAY(buf, bytesperrow); if (buf == NULL) pm_error("can't allocate memory for row buffer"); tuplerow = pnm_allocpamrow(pamP); for (row = 0; row < pamP->height; ++row) { int col; pnm_readpamrow(pamP, tuplerow); if (cht == NULL) { /* It's a direct, non-colormapped raster */ if (bitspersample == 8 || bitspersample == 16) fillRowOfWholeBytePixels(pamP, tuplerow, buf, photometric, tiffMaxval, bitspersample); else fillRowOfSubBytePixels(pamP, tuplerow, buf, tiffMaxval, photometric, fillorder, bitspersample, NULL); } else { /* It's a colormapped raster */ if (bitspersample == 8) { for (col = 0; col < pamP->width; ++col) { int si; int found; pnm_lookuptuple(pamP, cht, tuplerow[col], &found, &si); if (!found) pm_error("INTERNAL ERROR. We made a color map, and a " "color map we need is not in it! " "col=%d r=%lu g=%lu b=%lu", col, tuplerow[col][0], tuplerow[col][1], tuplerow[col][2]); buf[col] = (unsigned char) si; } } else { fillRowOfSubBytePixels(pamP, tuplerow, buf, 0, 0, fillorder, bitspersample, cht); } } if (TIFFWriteScanline(tif, buf, row, 0) < 0) pm_error("failed a scanline write on row %d", row); } pnm_freepamrow(tuplerow); free(buf); }