static void 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); } }
/* 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); }
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; }
void pnm_addtuplefreqoccurrence(struct pam * const pamP, tuple const value, tuplehash const tuplefreqhash, int * const firstOccurrenceP) { unsigned int const hashvalue = pnm_hashtuple(pamP, value); struct tupleint_list_item * p; for (p = tuplefreqhash[hashvalue]; p && !pnm_tupleequal(pamP, p->tupleint.tuple, value); p = p->next); if (p) { /* It's in the hash; just tally one more occurence */ ++p->tupleint.value; *firstOccurrenceP = FALSE; } else { struct tupleint_list_item * p; /* It's not in the hash yet, so add it */ *firstOccurrenceP = TRUE; p = allocTupleIntListItem(pamP); if (p == NULL) pm_error("out of memory computing hash table"); pnm_assigntuple(pamP, p->tupleint.tuple, value); p->tupleint.value = 1; p->next = tuplefreqhash[hashvalue]; tuplefreqhash[hashvalue] = p; } }
void pnm_addtotuplehash(struct pam * const pamP, tuplehash const tuplehash, tuple const tupletoadd, int const value, int * const fitsP) { /*---------------------------------------------------------------------------- Add a tuple value to the hash -- assume it isn't already there. Allocate new space for the tuple value and the hash chain element. If we can't allocate space for the new hash chain element, don't change anything and return *fitsP = FALSE; -----------------------------------------------------------------------------*/ struct tupleint_list_item * const listItemP = allocTupleIntListItem(pamP); if (listItemP == NULL) *fitsP = FALSE; else { unsigned int const hashvalue = pnm_hashtuple(pamP, tupletoadd); *fitsP = TRUE; pnm_assigntuple(pamP, listItemP->tupleint.tuple, tupletoadd); listItemP->tupleint.value = value; listItemP->next = tuplehash[hashvalue]; tuplehash[hashvalue] = listItemP; } }
static tupletable tuplehashtotable(const struct pam * const pamP, tuplehash const tuplehash, unsigned int const allocsize) { /*---------------------------------------------------------------------------- Create a tuple table containing the info from a tuple hash. Allocate space in the table for 'allocsize' elements even if there aren't that many tuple values in the input hash. That's so the caller has room for expansion. Caller must ensure that 'allocsize' is at least as many tuple values as there are in the input hash. We allocate new space for all the table contents; there are no pointers in the table to tuples or anything else in existing space. -----------------------------------------------------------------------------*/ tupletable tupletable; const char * error; alloctupletable(pamP, allocsize, &tupletable, &error); if (error) { pm_errormsg("%s", error); strfree(error); pm_longjmp(); } else { unsigned int i, j; /* Loop through the hash table. */ j = 0; for (i = 0; i < HASH_SIZE; ++i) { /* Walk this hash chain */ struct tupleint_list_item * p; for (p = tuplehash[i]; p; p = p->next) { assert(j < allocsize); tupletable[j]->value = p->tupleint.value; pnm_assigntuple(pamP, tupletable[j]->tuple, p->tupleint.tuple); ++j; } } } return tupletable; }
static void composeRow(int const originleft, struct pam * const underlayPamP, struct pam * const overlayPamP, bool const invertAlpha, float const masterOpacity, struct pam * const composedPamP, enum sampleScale const sampleScale, enum alphaMix const alphaMix, const tuple * const underlayTuplerow, const tuple * const overlayTuplerow, const tuplen * const alphaTuplerown, tuple * const composedTuplerow) { /*---------------------------------------------------------------------------- Create a row of tuples ('composedTupleRow') which is the composition of row 'overlayTupleRow' laid over row 'underlayTupleRow', starting at column 'originLeft'. *underlayPamP and *overlayPamP describe the respective tuple rows. -----------------------------------------------------------------------------*/ unsigned int col; for (col = 0; col < composedPamP->width; ++col) { int const ovlcol = col - originleft; if (ovlcol >= 0 && ovlcol < overlayPamP->width) { tuplen const alphaTuplen = alphaTuplerown ? alphaTuplerown[ovlcol] : NULL; overlayPixel(overlayTuplerow[ovlcol], overlayPamP, underlayTuplerow[col], underlayPamP, alphaTuplen, invertAlpha, composedTuplerow[col], composedPamP, masterOpacity, sampleScale, alphaMix); } else /* Overlay image does not touch this column. */ pnm_assigntuple(composedPamP, composedTuplerow[col], underlayTuplerow[col]); } }
static void addColorOccurrenceToHash(tuple const color, tuplehash const tuplefreqhash, struct pam * const pamP, unsigned int const maxsize, unsigned int * const sizeP, bool * const fullP) { unsigned int const hashvalue = pnm_hashtuple(pamP, color); struct tupleint_list_item *p; for (p = tuplefreqhash[hashvalue]; p && !pnm_tupleequal(pamP, p->tupleint.tuple, color); p = p->next); if (p) { /* It's in the hash; just tally one more occurence */ ++p->tupleint.value; *fullP = FALSE; } else { /* It's not in the hash yet, so add it (if allowed) */ ++(*sizeP); if (maxsize > 0 && *sizeP > maxsize) *fullP = TRUE; else { *fullP = FALSE; p = allocTupleIntListItem(pamP); if (p == NULL) pm_error("out of memory computing hash table"); pnm_assigntuple(pamP, p->tupleint.tuple, color); p->tupleint.value = 1; p->next = tuplefreqhash[hashvalue]; tuplefreqhash[hashvalue] = p; } } }
static void ditherRow(struct pam * const inpamP, const tuple * const inrow, const scaler * const scalerP, unsigned int ** const ditherMatrix, unsigned int const ditherMatrixArea, struct colorResolution const colorRes, unsigned int const row, unsigned int const modMask, struct pam * const outpamP, tuple * const outrow) { unsigned int col; for (col = 0; col < inpamP->width; ++col) { unsigned int const d = ditherMatrix[row & modMask][(inpamP->width-col-1) & modMask]; unsigned int dithered[3]; unsigned int plane; assert(inpamP->depth >= 3); for (plane = 0; plane < 3; ++plane) dithered[plane] = dither(inrow[col][plane], inpamP->maxval, d, colorRes.c[plane]-1, ditherMatrixArea); pnm_assigntuple(outpamP, outrow[col], scaler_scale(scalerP, dithered[PAM_RED_PLANE], dithered[PAM_GRN_PLANE], dithered[PAM_BLU_PLANE])); } }
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 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) { /*---------------------------------------------------------------------------- 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. -----------------------------------------------------------------------------*/ enum sampleScale const sampleScale = assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE; int underlayRow; /* NB may be negative */ int overlayRow; /* NB may be negative */ tuple * composedTuplerow; tuple * underlayTuplerow; tuple * overlayTuplerow; tuplen * alphaTuplerown; bool overlayHasOpacity; unsigned int opacityPlane; pnm_getopacity(overlayPamP, &overlayHasOpacity, &opacityPlane); composedTuplerow = pnm_allocpamrow(composedPamP); underlayTuplerow = pnm_allocpamrow(underlayPamP); overlayTuplerow = pnm_allocpamrow(overlayPamP); if (alphaPamP) alphaTuplerown = pnm_allocpamrown(alphaPamP); pnm_writepaminit(composedPamP); 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); adaptRowToOutputFormat(overlayPamP, overlayTuplerow, composedPamP); if (alphaPamP) pnm_readpamrown(alphaPamP, alphaTuplerown); } if (underlayRow >= 0 && underlayRow < underlayPamP->height) { pnm_readpamrow(underlayPamP, underlayTuplerow); adaptRowToOutputFormat(underlayPamP, underlayTuplerow, composedPamP); if (underlayRow < origintop || underlayRow >= origintop + overlayPamP->height) { /* Overlay image does not touch this underlay row. */ pnm_writepamrow(composedPamP, underlayTuplerow); } else { unsigned int col; for (col = 0; col < composedPamP->width; ++col) { int const ovlcol = col - originleft; if (ovlcol >= 0 && ovlcol < overlayPamP->width) { tuplen const alphaTuplen = alphaPamP ? alphaTuplerown[ovlcol] : NULL; overlayPixel(overlayTuplerow[ovlcol], overlayPamP, underlayTuplerow[col], underlayPamP, alphaTuplen, invertAlpha, overlayHasOpacity, opacityPlane, composedTuplerow[col], composedPamP, masterOpacity, sampleScale); } else /* Overlay image does not touch this column. */ pnm_assigntuple(composedPamP, composedTuplerow[col], underlayTuplerow[col]); } pnm_writepamrow(composedPamP, composedTuplerow); } } } pnm_freepamrow(composedTuplerow); pnm_freepamrow(underlayTuplerow); pnm_freepamrow(overlayTuplerow); if (alphaPamP) pnm_freepamrown(alphaTuplerown); }