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 colormapToSquare(struct pam * const pamP, tupletable2 const colormap, tuple *** const outputRasterP) { { unsigned int const intsqrt = (int)sqrt((float)colormap.size); if (intsqrt * intsqrt == colormap.size) pamP->width = intsqrt; else pamP->width = intsqrt + 1; } { unsigned int const intQuotient = colormap.size / pamP->width; if (pamP->width * intQuotient == colormap.size) pamP->height = intQuotient; else pamP->height = intQuotient + 1; } { tuple ** outputRaster; unsigned int row; unsigned int colormapIndex; outputRaster = pnm_allocpamarray(pamP); colormapIndex = 0; /* initial value */ for (row = 0; row < pamP->height; ++row) { unsigned int col; for (col = 0; col < pamP->width; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) { outputRaster[row][col][plane] = colormap.table[colormapIndex]->tuple[plane]; } if (colormapIndex < colormap.size-1) ++colormapIndex; } } *outputRasterP = outputRaster; } }
static void colormapToSingleRow(struct pam * const pamP, tupletable2 const colormap, tuple *** const outputRasterP) { tuple ** outputRaster; unsigned int col; pamP->width = colormap.size; pamP->height = 1; outputRaster = pnm_allocpamarray(pamP); for (col = 0; col < pamP->width; ++col) { int plane; for (plane = 0; plane < pamP->depth; ++plane) outputRaster[0][col][plane] = colormap.table[col]->tuple[plane]; } *outputRasterP = outputRaster; }
static void doHilbert(FILE * const ifP, unsigned int const clumpSize) { /*---------------------------------------------------------------------------- Use hilbert space filling curve dithering -----------------------------------------------------------------------------*/ /* * This is taken from the article "Digital Halftoning with * Space Filling Curves" by Luiz Velho, proceedings of * SIGRAPH '91, page 81. * * This is not a terribly efficient or quick version of * this algorithm, but it seems to work. - Graeme Gill. * [email protected] * */ struct pam graypam; struct pam bitpam; tuple ** grays; tuple ** bits; int end; int *x,*y; int sum; grays = pnm_readpam(ifP, &graypam, PAM_STRUCT_SIZE(tuple_type)); bitpam = makeOutputPam(graypam.width, graypam.height); bits = pnm_allocpamarray(&bitpam); MALLOCARRAY(x, clumpSize); MALLOCARRAY(y, clumpSize); if (x == NULL || y == NULL) pm_error("out of memory"); initHilbert(graypam.width, graypam.height); sum = 0; end = clumpSize; while (end == clumpSize) { unsigned int i; /* compute the next cluster co-ordinates along hilbert path */ for (i = 0; i < end; i++) { if (hilbert(&x[i],&y[i])==0) end = i; /* we reached the end */ } /* sum levels */ for (i = 0; i < end; i++) sum += grays[y[i]][x[i]][0]; /* dither half and half along path */ for (i = 0; i < end; i++) { unsigned int const row = y[i]; unsigned int const col = x[i]; if (sum >= graypam.maxval) { bits[row][col][0] = 1; sum -= graypam.maxval; } else bits[row][col][0] = 0; } } pnm_writepam(&bitpam, bits); pnm_freepamarray(bits, &bitpam); pnm_freepamarray(grays, &graypam); }
int main(int argc, char **argv) { struct cmdlineInfo cmdline; FILE * ifP; tuplen ** tuplenarray; struct pam inpam; struct pam mappam; tuple ** map; int row; float * sharpness; pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); tuplenarray = pnm_readpamn(ifP, &inpam, sizeof(inpam)); mappam = inpam; mappam.file = stdout; mappam.maxval = 255; MALLOCARRAY_NOFAIL(sharpness, inpam.depth); map = pnm_allocpamarray(&mappam); makeBlackRown(&inpam, tuplenarray[0]); for (row = 1; row < inpam.height-1; ++row) { int col; makeBlackTuplen(&inpam, tuplenarray[row][0]); for (col = 1; col < inpam.width-1; ++col) { int dy; unsigned int plane; for (plane = 0; plane < inpam.depth; ++plane) sharpness[plane] = 0.0; for (dy = -1; dy <= 1; ++dy) { int dx; for (dx = -1; dx <= 1; ++dx) { if (dx != 0 || dy != 0) { unsigned int plane; for (plane = 0; plane < inpam.depth; ++plane) { samplen const sampleval = tuplenarray[row][col][plane]; samplen const sampleval2 = tuplenarray[row+dy][col+dx][plane]; sharpness[plane] += fabs(sampleval - sampleval2); } } } } makeSharpnessPixel(&mappam, sharpness, map[row][col]); } makeBlackTuplen(&inpam, tuplenarray[row][inpam.width-1]); } makeBlackRown(&inpam, tuplenarray[inpam.height-1]); free(sharpness); pnm_writepam(&mappam, map); pnm_freepamarray(map, &mappam); pnm_freepamarrayn(tuplenarray, &inpam); return 0; }