/* ppmtopcx.c - convert a portable pixmap to PCX ** ** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) ** based on ppmtopcx.c by Michael Davidson ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. ** ** 11/Dec/94: first version ** 12/Dec/94: added support for "packed" format (16 colors or less) */ #include "ppm.h" #include "shhopt.h" /*#define DEBUG*/ #define MAXCOLORS 256 #define PCX_MAGIC 0x0a /* PCX magic number */ #define PCX_256_COLORS 0x0c /* magic number for 256 colors */ #define PCX_MAXVAL (pixval)255 /* prototypes */ static void PCXEncode ARGS(( FILE* fp, unsigned char* buf, int Size )); static void ToPlanes ARGS((unsigned char *rawrow, int width, unsigned char *buf, int planes)); static void PackBits ARGS((unsigned char *rawrow, int width, unsigned char *buf, int bits)); static void Putword ARGS(( int w, FILE* fp )); static void Putbyte ARGS(( int b, FILE* fp )); struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char *inputFilespec; /* '-' if stdin */ unsigned int truecolor; /* -24bit option */ unsigned int use_8_bit; /* -8bit option */ unsigned int packed; unsigned int verbose; int xpos; int ypos; }; static void parseCommandLine ( int argc, char ** argv, struct cmdlineInfo *cmdlineP ) { /*---------------------------------------------------------------------------- parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. If command line is internally inconsistent (invalid options, etc.), issue error message to stderr and abort program. Note that the strings we return are stored in the storage that was passed to us as the argv array. We also trash *argv. -----------------------------------------------------------------------------*/ optEntry *option_def = malloc( 100*sizeof( optEntry ) ); /* Instructions to optParseOptions3 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; /* Set defaults */ cmdlineP->xpos = 0; cmdlineP->ypos = 0; option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "24bit", OPT_FLAG, NULL, &cmdlineP->truecolor, 0 ); OPTENT3(0, "8bit", OPT_FLAG, NULL, &cmdlineP->use_8_bit, 0 ); OPTENT3(0, "packed", OPT_FLAG, NULL, &cmdlineP->packed, 0 ); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0 ); OPTENT3(0, "xpos", OPT_INT, &cmdlineP->xpos, NULL, 0 ); OPTENT3(0, "ypos", OPT_INT, &cmdlineP->ypos, NULL, 0 ); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); /* Uses and sets argc, argv, and some of *cmdline_p and others. */ if (cmdlineP->xpos < -32767 || cmdlineP->xpos > 32768) pm_error("-xpos value (%d) is outside acceptable range " "(-32767, 32768)", cmdlineP->xpos); if (cmdlineP->ypos < -32767 || cmdlineP->ypos > 32768) pm_error("-ypos value (%d) is outside acceptable range " "(-32767, 32768)", cmdlineP->ypos); if (argc-1 < 1) cmdlineP->inputFilespec = "-"; else if (argc-1 == 1) cmdlineP->inputFilespec = argv[1]; else pm_error("Program takes at most one argument " "(input file specification). You specified %d", argc-1); } static void write_header(FILE * const fp, int const cols, int const rows, int const BitsPerPixel, int const Planes, pixel * const cmap16, unsigned int const xPos, unsigned int const yPos) { int i, BytesPerLine; Putbyte( PCX_MAGIC, fp); /* .PCX magic number */ Putbyte( 0x05, fp); /* PC Paintbrush version */ Putbyte( 0x01, fp); /* .PCX run length encoding */ Putbyte( BitsPerPixel, fp); /* bits per pixel */ Putword( xPos, fp ); /* x1 - image left */ Putword( yPos, fp ); /* y1 - image top */ Putword( xPos+cols-1, fp ); /* x2 - image right */ Putword( yPos+rows-1, fp ); /* y2 - image bottom */ Putword( cols, fp ); /* horizontal resolution */ Putword( rows, fp ); /* vertical resolution */ /* Write out the Color Map for images with 16 colors or less */ if( cmap16 ) for (i = 0; i < 16; ++i) { Putbyte( PPM_GETR(cmap16[i]), fp ); Putbyte( PPM_GETG(cmap16[i]), fp ); Putbyte( PPM_GETB(cmap16[i]), fp ); } else for (i = 0; i < 16; ++i) { Putbyte( 0, fp ); Putbyte( 0, fp ); Putbyte( 0, fp ); } Putbyte( 0, fp); /* reserved byte */ Putbyte( Planes, fp); /* number of color planes */ BytesPerLine = ((cols * BitsPerPixel) + 7) / 8; Putword( BytesPerLine, fp ); /* number of bytes per scanline */ Putword( 1, fp); /* palette info */ for (i = 0; i < 58; ++i) /* fill to end of header */ Putbyte( 0, fp ); } static void ppm_to_16col_pcx(pixel ** const pixels, int const cols, int const rows, pixel * const cmap, int const colors, colorhash_table const cht, bool const packbits, unsigned int const xPos, unsigned int const yPos) { int Planes, BytesPerLine, BitsPerPixel; unsigned char *rawrow, *planesrow; register int col, row; #ifdef DEBUG pm_message("ppm -> %d colors", colors); #endif if( packbits ) { Planes = 1; if( colors > 4 ) BitsPerPixel = 4; else if( colors > 2 ) BitsPerPixel = 2; else BitsPerPixel = 1; } else { BitsPerPixel = 1; if( colors > 8 ) Planes = 4; else if( colors > 4 ) Planes = 3; else if( colors > 2 ) Planes = 2; else Planes = 1; } BytesPerLine = ((cols * BitsPerPixel) + 7) / 8; rawrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); planesrow = (unsigned char *) pm_allocrow(BytesPerLine, sizeof(unsigned char)); #ifdef DEBUG pm_message("Planes = %d, BitsPerPixel = %d, BytesPerLine = %d", Planes, BitsPerPixel, BytesPerLine); #endif write_header(stdout, cols, rows, BitsPerPixel, Planes, cmap, xPos, yPos); for( row = 0; row < rows; row++ ) { register pixel *pP = pixels[row]; for( col = 0; col < cols; col++, pP++ ) rawrow[col] = (unsigned char)ppm_lookupcolor(cht, pP) & 0x0f; if( packbits ) { PackBits(rawrow, cols, planesrow, BitsPerPixel); PCXEncode(stdout, planesrow, BytesPerLine); } else { for( col = 0; col < Planes; col++ ) { ToPlanes(rawrow, cols, planesrow, col); PCXEncode(stdout, planesrow, BytesPerLine); } } } #ifdef DEBUG pm_message("done!"); #endif pm_freerow((void*)planesrow); pm_freerow((void*)rawrow); } static void ppm_to_256col_pcx(pixel ** const pixels, int const cols, int const rows, pixel * const cmap, int const colors, colorhash_table const cht, unsigned int const xPos, unsigned int const yPos) { int col, row, i; unsigned char *rawrow; #ifdef DEBUG pm_message("ppm -> 256 color, writing index array..."); #endif rawrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); /* 8 bits per pixel, 1 plane */ write_header(stdout, cols, rows, 8, 1, (pixel *)NULL, xPos, yPos); for( row = 0; row < rows; row++ ) { register pixel *pP = pixels[row]; for( col = 0; col < cols; col++, pP++ ) rawrow[col] = ppm_lookupcolor(cht, pP); PCXEncode(stdout, rawrow, cols); } #ifdef DEBUG pm_message("ok, writing colormap..."); #endif Putbyte(PCX_256_COLORS, stdout); for( i = 0; i < MAXCOLORS; i++ ) { Putbyte(PPM_GETR(cmap[i]), stdout); Putbyte(PPM_GETG(cmap[i]), stdout); Putbyte(PPM_GETB(cmap[i]), stdout); } #ifdef DEBUG pm_message("done!"); #endif pm_freerow((void*)rawrow); } static void ppm_to_truecol_pcx(pixel ** const pixels, int const cols, int const rows, pixval const maxval, unsigned int const xPos, unsigned int const yPos) { unsigned char *redrow, *greenrow, *bluerow; int col, row; #ifdef DEBUG pm_message("ppm -> 24bit"); #endif redrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); greenrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); bluerow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); /* 8 bits per pixel, 3 planes */ write_header(stdout, cols, rows, 8, 3, (pixel *)NULL, xPos, yPos); for( row = 0; row < rows; row++ ) { register pixel *pP = pixels[row]; for( col = 0; col < cols; col++, pP++ ) { if( maxval != PCX_MAXVAL ) { redrow[col] = (long)PPM_GETR(*pP) * PCX_MAXVAL / maxval; greenrow[col] = (long)PPM_GETG(*pP) * PCX_MAXVAL / maxval; bluerow[col] = (long)PPM_GETB(*pP) * PCX_MAXVAL / maxval; } else { redrow[col] = PPM_GETR(*pP); greenrow[col] = PPM_GETG(*pP); bluerow[col] = PPM_GETB(*pP); } } PCXEncode(stdout, redrow, cols); PCXEncode(stdout, greenrow, cols); PCXEncode(stdout, bluerow, cols); } #ifdef DEBUG pm_message("done!"); #endif pm_freerow((void*)bluerow); pm_freerow((void*)greenrow); pm_freerow((void*)redrow); } static void ppm_to_palette_pcx(pixel ** const pixels, int const cols, int const rows, pixval const maxval, unsigned int const xPos, unsigned int const yPos, colorhist_vector const chv, int const colors, bool const packbits, bool const use_8_bit) { pixel black_pixel, *cmap; int colors2; colorhash_table cht; int i; colors2 = colors; /* initial value */ /* Force black to slot 0 if possible. */ PPM_ASSIGN(black_pixel, 0, 0, 0 ); ppm_addtocolorhist(chv, &colors2, MAXCOLORS, &black_pixel, 0, 0 ); /* build colormap */ cmap = ppm_allocrow(MAXCOLORS); for( i = 0; i < colors2; i++ ) { pixval r, g, b; r = PPM_GETR(chv[i].color); g = PPM_GETG(chv[i].color); b = PPM_GETB(chv[i].color); if( maxval != PCX_MAXVAL ) { r = r * PCX_MAXVAL / maxval; g = g * PCX_MAXVAL / maxval; b = b * PCX_MAXVAL / maxval; } PPM_ASSIGN(cmap[i], r, g, b); } for( ; i < MAXCOLORS; i++ ) PPM_ASSIGN(cmap[i], 0,0,0); /* make a hash table for fast color lookup */ cht = ppm_colorhisttocolorhash(chv, colors2); ppm_freecolorhist( chv ); /* convert image */ if( colors2 <= 16 && !use_8_bit ) ppm_to_16col_pcx(pixels, cols, rows, cmap, colors2, cht, packbits, xPos, yPos); else ppm_to_256col_pcx(pixels, cols, rows, cmap, colors2, cht, xPos, yPos); } int main(int argc, char *argv[]) { struct cmdlineInfo cmdline; FILE* ifP; int rows, cols, colors; pixval maxval; pixel **pixels; colorhist_vector chv; bool truecolor; ppm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pixels = ppm_readppm(ifP, &cols, &rows, &maxval); pm_close(ifP); if (cmdline.truecolor) truecolor = TRUE; else { /* Figure out the colormap. */ pm_message("computing colormap..."); chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); if (chv == NULL) { pm_message("too many colors - writing a 24bit PCX file"); pm_message("if you want a non-24bit file, " " a \'pnmquant %d\'", MAXCOLORS); truecolor = TRUE; } else { truecolor = FALSE; pm_message("%d colors found", colors); } } if (maxval != PCX_MAXVAL) pm_message("maxval is not %d - automatically rescaling colors", PCX_MAXVAL); if (truecolor) ppm_to_truecol_pcx(pixels, cols, rows, maxval, cmdline.xpos, cmdline.ypos); else ppm_to_palette_pcx(pixels, cols, rows, maxval, cmdline.xpos, cmdline.ypos, chv, colors, cmdline.packed, cmdline.use_8_bit); exit(0); } static void PCXEncode(fp, buf, Size) FILE *fp; unsigned char *buf; int Size; { unsigned char *end; int c, previous, count; end = buf + Size; previous = *buf++; count = 1; while (buf < end) { c = *buf++; if (c == previous && count < 63) { ++count; continue; } if (count > 1 || (previous & 0xc0) == 0xc0) { count |= 0xc0; Putbyte ( count , fp ); } Putbyte(previous, fp); previous = c; count = 1; } if (count > 1 || (previous & 0xc0) == 0xc0) { count |= 0xc0; Putbyte ( count , fp ); } Putbyte(previous, fp); } static void PackBits(rawrow, width, buf, bits) unsigned char *rawrow; int width; unsigned char *buf; int bits; { register int x, i, shift; shift = i = -1; for( x = 0; x < width; x++ ) { if( shift < 0 ) { shift = 8-bits; buf[++i] = 0; } buf[i] |= (rawrow[x] << shift); shift -= bits; } } static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128}; static void ToPlanes(rawrow, width, buf, plane) unsigned char *rawrow; int width; unsigned char *buf; int plane; { int cbit, x, mask; unsigned char *cp = buf-1; mask = 1 << plane; cbit = -1; for( x = 0; x < width; x++ ) { if( cbit < 0 ) { cbit = 7; *++cp = 0; } if( rawrow[x] & mask ) *cp |= bitmask[cbit]; --cbit; } } /* * Write out a two-byte little-endian word to the PCX file */ static void Putword(int const w, FILE * const fp) { int rc; rc = pm_writelittleshort(fp, w); if (rc != 0) pm_error("Error writing integer to output file"); } /* * Write out a byte to the PCX file */ static void Putbyte(int b, FILE * const fp) { if( fputc( b & 0xff, fp ) == EOF ) pm_error("Error writing byte to output file."); }