/* ppmtopcl.c - convert PPM into PCL language for HP PaintJet and * PaintJet XL color printers * AJCD 12/3/91 * * usage: * ppmtopcl [-nopack] [-gamma ] [-presentation] [-dark] * [-diffuse] [-cluster] [-dither] * [-xshift ] [-yshift ] * [-xshift ] [-yshift ] * [-xsize|-width|-xscale ] [-ysize|-height|-yscale ] * [ppmfile] * */ #include #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "ppm.h" #include "runlength.h" #define MAXCOLORS 1024 const char * const usage="[-nopack] [-gamma ] [-presentation] [-dark]\n\ [-diffuse] [-cluster] [-dither]\n\ [-xshift ] [-yshift ]\n\ [-xshift ] [-yshift ]\n\ [-xsize|-width|-xscale ] [-ysize|-height|-yscale ]\n\ [ppmfile]"; #define PCL_MAXWIDTH 2048 #define PCL_MAXHEIGHT 32767 #define PCL_MAXVAL 255 static bool nopack = false; static int dark = 0; static int diffuse = 0; static int dither = 0; static int cluster = 0; static int xsize = 0; static int ysize = 0; static int xshift = 0; static int yshift = 0; static int quality = 0; static double xscale = 0.0; static double yscale = 0.0; static double gammaVal = 0.0; /* argument types */ #define DIM 0 #define REAL 1 #define BOOL 2 static const struct options { const char *name; int type; void *value; } options[] = { {"-gamma", REAL, &gammaVal }, {"-presentation", BOOL, &quality }, {"-width", DIM, &xsize }, {"-xsize", DIM, &xsize }, {"-height", DIM, &ysize }, {"-ysize", DIM, &ysize }, {"-xscale", REAL, &xscale }, {"-yscale", REAL, &yscale }, {"-xshift", DIM, &xshift }, {"-yshift", DIM, &yshift }, {"-dark", BOOL, &dark }, {"-diffuse", BOOL, &diffuse }, {"-dither", BOOL, &dither }, {"-cluster", BOOL, &cluster }, {"-nopack", BOOL, &nopack }, }; static void putword(unsigned short const w) { putchar((w >> 8) & 0xff); putchar((w >> 0) & 0xff); } static unsigned int bitwidth(unsigned int v) { unsigned int bpp; /* calculate # bits for value */ for (bpp = 0; v > 0; ) { ++bpp; v >>= 1; } return bpp; } /* The following belong to the bit putter. They really should be in a struct passed to the methods of the bit putter instead. */ static char *inrow; static char *outrow; /* "signed" was commented out below, but it caused warnings on an SGI compiler, which defaulted to unsigned character. 2001.03.30 BJH */ static signed char * runcnt; static int out = 0; static int cnt = 0; static int num = 0; static bool pack = false; static void initbits(unsigned int const bytesPerRow) { MALLOCARRAY(inrow, bytesPerRow); MALLOCARRAY(outrow, bytesPerRow * 2); MALLOCARRAY(runcnt, bytesPerRow); if (!inrow || !outrow || !runcnt) pm_error("can't allocate space for row"); } static void termbits() { free(runcnt); free(outrow); free(inrow); } static void putbits(unsigned int const bArg, unsigned int const nArg) { /*---------------------------------------------------------------------------- Add 'bArg' to byte-packing output buffer as 'n' bits. n should never be > 8 -----------------------------------------------------------------------------*/ int b; int n; int xo; int xc; b = bArg; n = nArg; assert(n <= 8); if (cnt + n > 8) { /* overflowing current byte? */ xc = cnt + n - 8; xo = (b & ~(-1 << xc)) << (8-xc); n -= xc; b >>= xc; } else { xo = 0; xc = 0; } cnt += n; out |= (b & ~(-1 << n)) << (8-cnt); if (cnt >= 8) { inrow[num++] = out; out = xo; cnt = xc; } } static void flushbits() { /*---------------------------------------------------------------------------- flush a row of buffered bits. -----------------------------------------------------------------------------*/ if (cnt) { inrow[num++] = out; out = cnt = 0; } for (; num > 0 && inrow[num-1] == 0; --num); /* remove trailing zeros */ printf("\033*b"); if (num && !nopack) { /* TIFF 4.0 packbits encoding */ size_t outSize; pm_rlenc_compressbyte( (unsigned char *)inrow, (unsigned char *)outrow, PM_RLE_PACKBITS, num, &outSize); if (outSize < num) { num = outSize; if (!pack) { printf("2m"); pack = true; } } else { if (pack) { printf("0m"); pack = false; } } } printf("%dW", num); { unsigned int i; for (i = 0; i < num; ++i) putchar(pack ? outrow[i] : inrow[i]); } num = 0; /* new row */ } static void computeColormap(pixel ** const pixels, unsigned int const cols, unsigned int const rows, unsigned int const maxColors, colorhist_vector * const chvP, colorhash_table * const chtP, unsigned int * const colorCtP) { colorhist_vector chv; colorhash_table cht; int colorCt; pm_message("Computing colormap..."); chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colorCt); if (!chv) pm_error("too many colors; reduce to %u or fewer with 'pnmquant'", MAXCOLORS); pm_message("... Done. %u colors found.", colorCt); /* And make a hash table for fast lookup. */ cht = ppm_colorhisttocolorhash(chv, colorCt); *chvP = chv; *chtP = cht; *colorCtP = colorCt; } static unsigned int nextPowerOf2(unsigned int const arg) { /*---------------------------------------------------------------------------- Works only on 0-7 -----------------------------------------------------------------------------*/ switch (arg) { /* round up to 1,2,4,8 */ case 0: return 0; break; case 1: return 1; break; case 2: return 2; break; case 3: case 4: return 4; break; case 5: case 6: case 7: case 8: return 8; break; default: assert(false); } } static void computeColorDownloadingMode(unsigned int const colorCt, unsigned int const cols, pixval const maxval, unsigned int * const bytesPerRowP, bool * const colorMappedP, unsigned int * const bitsPerPixelRedP, unsigned int * const bitsPerPixelGrnP, unsigned int * const bitsPerPixelBluP, unsigned int * const bitsPerIndexP) { /*---------------------------------------------------------------------------- -----------------------------------------------------------------------------*/ unsigned int const indexBitCt = bitwidth(colorCt); assert(colorCt > 0); assert(indexBitCt > 0); if (indexBitCt > 8) { /* Can't use indexed mode */ /* We will instead write 1-3 full bytes per pixel, with those bytes divided into red bits, green bits, and blue bits. We pad the red bits as needed to fill out whole bytes. We stick to 1, 2, 4, or 8 bits per pixel only because this program's bit writer can't handle more than 8, which would happen with those padded red fields if we allowed e.g. 7 bits for green and blue (ergo 10 bits for red). */ unsigned int const bitsPerSample = nextPowerOf2(bitwidth(maxval)); unsigned int const bitsPerPixel = ROUNDUP(3 * bitsPerSample, 8); unsigned int const bytesPerPixel = bitsPerPixel / 8; *colorMappedP = false; *bitsPerPixelGrnP = bitsPerSample; *bitsPerPixelBluP = bitsPerSample; *bitsPerPixelRedP = bitsPerPixel - *bitsPerPixelGrnP - *bitsPerPixelBluP; *bytesPerRowP = bytesPerPixel * cols; } else { unsigned int const bitsPerPixel = nextPowerOf2(indexBitCt); unsigned int pixelsPerByte; *colorMappedP = true; *bitsPerIndexP = bitsPerPixel; pixelsPerByte = 8 / bitsPerPixel; *bytesPerRowP = (cols + pixelsPerByte - 1) / pixelsPerByte; } if (*colorMappedP) pm_message("Writing %u bit color indices", *bitsPerIndexP); else pm_message("Writing direct color, %u red bits, %u green, %u blue", *bitsPerPixelRedP, *bitsPerPixelGrnP, *bitsPerPixelBluP); } static void writePclHeader(unsigned int const cols, unsigned int const rows, pixval const maxval, int const xshift, int const yshift, unsigned int const quality, unsigned int const xsize, unsigned int const ysize, double const gammaVal, unsigned int const dark, unsigned int const render, bool const colorMapped, unsigned int const bitsPerPixelRed, unsigned int const bitsPerPixelGrn, unsigned int const bitsPerPixelBlu, unsigned int const bitsPerIndex) { #if 0 printf("\033&l26A"); /* paper size */ #endif printf("\033*r%us%uT", cols, rows); /* source width, height */ if (xshift != 0 || yshift != 0) printf("\033&a%+dh%+dV", xshift, yshift); /* xshift, yshift */ if (quality) printf("\033*o%uQ", quality); /* print quality */ printf("\033*t"); if (xsize == 0 && ysize == 0) printf("180r"); /* resolution */ else { /* destination width, height */ if (xsize != 0) printf("%uh", xsize); if (ysize != 0) printf("%uv", ysize); } if (gammaVal != 0) printf("%.3fi", gammaVal); /* gamma correction */ if (dark) printf("%uk", dark); /* scaling algorithms */ printf("%uJ", render); /* rendering algorithm */ printf("\033*v18W"); /* configure image data */ putchar(0); /* relative colors */ putchar(colorMapped ? 1 : 3); /* index/direct pixel mode */ putchar(bitsPerIndex); /* ignored in direct pixel mode */ putchar(colorMapped ? 0 : bitsPerPixelRed); putchar(colorMapped ? 0 : bitsPerPixelGrn); putchar(colorMapped ? 0 : bitsPerPixelBlu); putword(maxval); /* max red reference */ putword(maxval); /* max green reference */ putword(maxval); /* max blue reference */ putword(0); /* min red reference */ putword(0); /* min green reference */ putword(0); /* min blue reference */ } static void writePalette(colorhist_vector const chv, unsigned int const colorCt) { unsigned int i; for (i = 0; i < colorCt; ++i) { unsigned int const r = PPM_GETR( chv[i].color); unsigned int const g = PPM_GETG( chv[i].color); unsigned int const b = PPM_GETB( chv[i].color); if (i == 0) printf("\033*v"); if (r) printf("%ua", r); if (g) printf("%ub", g); if (b) printf("%uc", b); if (i == colorCt - 1) printf("%uI", i); /* assign color index */ else printf("%ui", i); /* assign color index */ } } static void writeRaster(pixel ** const pixels, unsigned int const rows, unsigned int const cols, colorhash_table const cht, bool const colorMapped, unsigned int const bitsPerIndex, unsigned int const bitsPerPixelRed, unsigned int const bitsPerPixelGrn, unsigned int const bitsPerPixelBlu) { unsigned int row; for (row = 0; row < rows; ++row) { pixel * const pixrow = pixels[row]; if (colorMapped) { unsigned int col; for (col = 0; col < cols; ++col) putbits(ppm_lookupcolor(cht, &pixrow[col]), bitsPerIndex); flushbits(); } else { unsigned int col; for (col = 0; col < cols; ++col) { putbits(PPM_GETR(pixrow[col]), bitsPerPixelRed); putbits(PPM_GETG(pixrow[col]), bitsPerPixelGrn); putbits(PPM_GETB(pixrow[col]), bitsPerPixelBlu); /* don't need to flush */ } flushbits(); } } } int main(int argc, const char * argv[]) { FILE * ifP; pixel ** pixels; int rows, cols; pixval maxval; bool colorMapped; unsigned int bytesPerRow; unsigned int bitsPerPixelRed, bitsPerPixelGrn, bitsPerPixelBlu; unsigned int bitsPerIndex; int render; unsigned int colorCt; colorhist_vector chv; colorhash_table cht; pm_proginit(&argc, argv); while (argc > 1 && argv[1][0] == '-') { unsigned int i; for (i = 0; i < sizeof(options)/sizeof(struct options); i++) { if (pm_keymatch(argv[1], options[i].name, MIN(strlen(argv[1]), strlen(options[i].name)))) { const char * c; switch (options[i].type) { case DIM: if (++argv, --argc == 1) pm_usage(usage); for (c = argv[1]; ISDIGIT(*c); c++); if (c[0] == 'p' && c[1] == 't') /* points */ *(int *)(options[i].value) = atoi(argv[1])*10; else if (c[0] == 'd' && c[1] == 'p') /* decipoints */ *(int *)(options[i].value) = atoi(argv[1]); else if (c[0] == 'i' && c[1] == 'n') /* inches */ *(int *)(options[i].value) = atoi(argv[1])*720; else if (c[0] == 'c' && c[1] == 'm') /* centimetres */ *(int *)(options[i].value) = atoi(argv[1])*283.46457; else if (!c[0]) /* dots */ *(int *)(options[i].value) = atoi(argv[1])*4; else pm_error("illegal unit of measure %s", c); break; case REAL: if (++argv, --argc == 1) pm_usage(usage); *(double *)(options[i].value) = atof(argv[1]); break; case BOOL: *(int *)(options[i].value) = 1; break; } break; } } if (i >= sizeof(options)/sizeof(struct options)) pm_usage(usage); argv++; argc--; } if (argc > 2) pm_usage(usage); else if (argc == 2) ifP = pm_openr(argv[1]); else ifP = stdin ; /* validate arguments */ if (diffuse+cluster+dither > 1) pm_error("only one of -diffuse, -dither and -cluster may be used"); render = diffuse ? 4 : dither ? 3 : cluster ? 7 : 0; if (xsize != 0.0 && xscale != 0.0) pm_error("only one of -xsize and -xscale may be used"); if (ysize != 0.0 && yscale != 0.0) pm_error("only one of -ysize and -yscale may be used"); pixels = ppm_readppm(ifP, &cols, &rows, &maxval); pm_close(ifP); /* limit checks */ if (cols > PCL_MAXWIDTH || rows > PCL_MAXHEIGHT) pm_error("image too large; reduce with ppmscale"); if (maxval > PCL_MAXVAL) pm_error("color range too large; reduce with ppmcscale"); computeColormap(pixels, cols, rows, MAXCOLORS, &chv, &cht, &colorCt); computeColorDownloadingMode( colorCt, cols, maxval, &bytesPerRow, &colorMapped, &bitsPerPixelRed, &bitsPerPixelGrn, &bitsPerPixelBlu, &bitsPerIndex); initbits(bytesPerRow); /* set up image details */ if (xscale != 0.0) xsize = cols * xscale * 4; if (yscale != 0.0) ysize = rows * yscale * 4; writePclHeader(cols, rows, maxval, xshift, yshift, quality, xsize, ysize, gammaVal, dark, render, colorMapped, bitsPerPixelRed, bitsPerPixelGrn, bitsPerPixelBlu, bitsPerIndex); if (colorMapped) writePalette(chv, colorCt); /* start raster graphics at CAP */ printf("\033*r%dA", (xsize != 0 || ysize != 0) ? 3 : 1); writeRaster(pixels, rows, cols, cht, colorMapped, bitsPerIndex, bitsPerPixelRed, bitsPerPixelGrn, bitsPerPixelBlu); printf("\033*rC"); /* end raster graphics */ ppm_freecolorhash(cht); ppm_freecolorhist(chv); termbits(); return 0; }