/* ** pcx.c - load a ZSoft PC Paintbrush (PCX) file for use inside xloadimage ** ** Tim Northrup ** Adapted from code by Jef Poskanzer (see Copyright below). ** ** Version 0.1 -- 4/25/91 -- Initial cut ** ** Copyright (c) 1991 Tim Northrup ** (see file "tgncpyrght.h" for complete copyright information) */ /* ** Copyright (C) 1988 by Jef Poskanzer. ** ** 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. ** ** This program (pcxtopbm) is based on the pcx2rf program by: ** Mike Macgirvin ** Stanford Relativity Gyro Program GP-B ** Stanford, Calif. 94503 ** ARPA: mike@relgyro.stanford.edu */ #include #include "image.h" #include "tgncpyrght.h" #include "pcx.h" /* ** pcxIdent ** ** Identify passed file as a PC Paintbrush image or not ** ** Returns 1 if file is a PCX file, 0 otherwise */ unsigned int pcxIdent ( fullname, name ) char *fullname, *name; { ZFILE *zf; unsigned int ret; int xmin; int xmax; int ymin; int ymax; int colors; char *type; ret = 0; if (! (zf = zopen ( fullname ))) return ( 0 ); PCXH = (PCXHeader *) lmalloc ( PCXHsize ); if (zread ( zf, PCXH, PCXHsize ) == PCXHsize) { if ((PCXH->Zid == PCX_MAGIC) && (PCXH->Zver <= 5)) { xmin = Word ( PCXH->Zxminlo, PCXH->Zxminhi); xmax = Word ( PCXH->Zxmaxlo, PCXH->Zxmaxhi); ymin = Word ( PCXH->Zyminlo, PCXH->Zyminhi); ymax = Word ( PCXH->Zymaxlo, PCXH->Zymaxhi); xmax = xmax - xmin + 1; ymax = ymax - ymin + 1; colors = 1 << (PCXH->Zbpp * PCXH->Znplanes); type = " PC Paintbrush image\n"; if (colors == 2) fprintf(stderr, "%s is a %dx%d monochrome%s", name, xmax, ymax, type ); else fprintf(stderr, "%s is a %dx%d %d color%s", name, xmax, ymax, colors, type ); ret = 1; } } zclose ( zf ); lfree ( PCXH ); return ( ret ); } /* ** PCX_Load_Raster ** ** Load raster image data into passed image structure. Raster Data ** ** means : Every bit is a pixel (if depth is 1) or every byte is a ** ** pixel (if depth is 8). ** ** Returns no value (void function) */ static void PCX_Load_Raster ( zf, image, depth ) ZFILE *zf; Image *image; int depth; /* Assertion : depth is 1 or 8 */ { byte *ptr = &(image->data[0]); int row = 0; int by_this_row = 0; int by_per_row; int linelen; int corrector; int i, b, cnt; by_per_row = Word ( PCXH->Zbprlo, PCXH->Zbprhi); if (depth == 1) linelen = (image->width / 8) + (image->width % 8 ? 1 : 0); else linelen = image->width; corrector = by_per_row - linelen; /* bytes per row is always even, which means that there is an */ /* excess byte if an odd nr of bytes would be sufficient. */ /* As newBitImage allocated memory without this excess, we have */ /* to read one less. But as there are two cases of reading, */ /* inside and outside of a run, I correct it afterwards, when */ /* the line is complete. */ while ((b = zgetc(zf)) != EOF) { /* Are we done ? */ if ((b & 0xC0) == 0xC0) { /* have a rep. count */ cnt = b & 0x3F; /* mask rep. bits out */ b = zgetc ( zf ); /* get real bits */ if (b == EOF) { /* Shouldn't happen ! */ fprintf(stderr, "Unexpected EOF\n" ); return; } } else cnt = 1; /* no repeating this one*/ if (depth ==1) b = 255 - b; /* Have to invert */ for ( i = 0; i < cnt; i++ ) { /* store count times */ *ptr++ = (byte) b; if (++by_this_row == by_per_row) { row++; /* start of a new line */ by_this_row = 0; /* reset counter */ if (corrector) ptr--; /*evtlly correct pointer*/ if ( row >= image->height ) { #if 0 /* happens a lot on valid images - jimf 10.28.91 */ if (depth == 1) /* Color : Map follows */ fprintf(stderr, "Warning: excess data ignored\n"); #endif return; } } } } } /* ** PCX_Planes ** ** Load plane image data into passed image structure. Plane data ** ** means : There are N planes, each containing M bits of a pixel ** ** byte, where N * M is not greater than 8 and M divides 8. (The ** ** cases M = 1 or 8, N = 1 are covered by PCX_Load_Raster). ** ** Returns no value (void function) */ static void PCX_Planes ( zf, image, bpp, nr_pl ) ZFILE *zf; Image *image; int bpp, nr_pl; /* Assertion : */ /* bpp is 1, 2 or 4 only, dividing 8 without remainder anyways, */ /* bpp * nr_pl <= 8 */ { byte *ptr, *sptr; register byte *tptr; int row = 0; int by_this_row = 0; int by_per_row; int this_plane = 0; int shifter = 0; register int j; int i, b, cnt; ptr = &(image->data[0]); by_per_row = Word ( PCXH->Zbprlo, PCXH->Zbprhi); sptr = tptr = (byte *) lcalloc ( by_per_row*8 ); /* We can't correct as easy as above, because we handle several */ /* bit planes simultaneously, and we must not load beyond row */ /* limits. So we load into a temporary row and copy it into */ /* image structure when the row is completed. */ while ((b = zgetc(zf)) != EOF) { if ((b & 0xC0) == 0xC0) { /* Get count and data as above */ cnt = b & 0x3F; b = zgetc ( zf ); if (b == EOF) { fprintf(stderr, "Unexpected EOF\n" ); return; } } else cnt = 1; for ( i = 0; i < cnt; i++ ) { /* Load data into temp. */ switch (bpp) { case 1 : *tptr++ |= (byte) (((b & 0x80 ) >> 7) << shifter); *tptr++ |= (byte) (((b & 0x40 ) >> 6) << shifter); *tptr++ |= (byte) (((b & 0x20 ) >> 5) << shifter); *tptr++ |= (byte) (((b & 0x10 ) >> 4) << shifter); *tptr++ |= (byte) (((b & 0x08 ) >> 3) << shifter); *tptr++ |= (byte) (((b & 0x04 ) >> 2) << shifter); *tptr++ |= (byte) (((b & 0x02 ) >> 1) << shifter); *tptr++ |= (byte) ((b & 0x01 ) << shifter); /* This is not a loop for performance reasons. */ /* Can't write e.g. ">> 2 - shifter", because */ /* that expression would get negative. */ break; case 2 : *tptr++ |= (byte) (((b & 0xC0 ) >> 6) << shifter); *tptr++ |= (byte) (((b & 0x30 ) >> 4) << shifter); *tptr++ |= (byte) (((b & 0x0C ) >> 2) << shifter); *tptr++ |= (byte) ((b & 0x03 ) << shifter); break; case 4 : *tptr++ |= (byte) (((b & 0xF0) >> 4) << shifter); *tptr++ |= (byte) ((b & 0x0F) << shifter); break; default :; /* Can't happen if assertion holds */ } if (++by_this_row == by_per_row) { /* Row done ? */ by_this_row = 0; /* reset counter */ if (++this_plane == nr_pl) { /* was it last plane ? */ row++; /* inc counter */ tptr = sptr; /* get saved ptr */ this_plane = shifter = 0;/* reset plane Nr */ for (j = 0; j < image->width; j++) { *ptr++ = *tptr; /* store final data */ *tptr++ = 0; /* clear temp data */ } if ( row >= image->height ) return; /* Done with image ? */ } else /* Prepare next plane */ shifter = this_plane * bpp; tptr = sptr; /* Get saved ptr */ } } } } /* ** PCX_LoadImage ** ** Load PC Paintbrush file into the passed Image structure. ** ** Returns no value (void function) */ static void PCX_LoadImage ( zf ,image ) ZFILE *zf; Image *image; { switch (PCXH->Zbpp) { /* What kind of plane do we have ? */ case 1 : /* Bit planes */ if (PCXH->Znplanes == 1) /* Only one : Read it */ PCX_Load_Raster ( zf, image, 1 ); else PCX_Planes ( zf, image, 1, PCXH->Znplanes ); break; case 2 : /* Two or four bits per plane */ case 4 : /* are read plane by plane */ PCX_Planes ( zf, image, PCXH->Zbpp, PCXH->Znplanes ); break; case 8 : /* Byte planes */ if (PCXH->Znplanes == 1) /* Only one : Read it */ PCX_Load_Raster ( zf, image, 8 ); else { /* More not allowed */ fprintf(stderr, "Only 1 plane allowed if 8 bits per plane\n"); cleanup(-1); } break; default : /* Neither case found */ fprintf(stderr, "%d bits per plane not supported\n", PCXH->Zbpp ); cleanup(-1); } } /* ** pcxLoad ** ** Load PCX Paintbrush file into an Image structure. ** ** Returns pointer to allocated struct if successful, NULL otherwise */ Image *pcxLoad ( fullname, name, verbose ) char *fullname, *name; unsigned int verbose; { ZFILE *zf; unsigned int i; int xmin; int xmax; int ymin; int ymax; int colors; PCXcolor *cmap; int clen; Image *image; if ( ! (zf = zopen ( fullname ))) /* Open input file */ return ( (Image *) NULL); PCXH = (PCXHeader *) lmalloc ( PCXHsize ); if (zread ( zf, PCXH, PCXHsize ) != PCXHsize) { /* Read header */ zclose ( zf ); return ( (Image *) NULL ); } if ((PCXH->Zid != PCX_MAGIC) || (PCXH->Zver > 5)) { zclose ( zf ); /* Is it PCX, Version less 5 ? */ return ( (Image *) NULL ); } znocache(zf); /* don't need caching anymore */ xmin = Word ( PCXH->Zxminlo, PCXH->Zxminhi); /* Calculate sizes */ xmax = Word ( PCXH->Zxmaxlo, PCXH->Zxmaxhi); ymin = Word ( PCXH->Zyminlo, PCXH->Zyminhi); ymax = Word ( PCXH->Zymaxlo, PCXH->Zymaxhi); xmax = xmax - xmin + 1; ymax = ymax - ymin + 1; colors = 1 << (PCXH->Zbpp * PCXH->Znplanes); /* Calculate colors*/ if (verbose) { /* Print Information */ if (colors == 2) fprintf(stderr, "%s is a %dx%d monochrome PC Paintbrush image\n", name, xmax, ymax ); else fprintf(stderr, "%s is a %dx%d %d color PC Paintbrush image\n", name, xmax, ymax, colors ); } if (colors > 256) { fprintf(stderr, "No more than 256 colors allowed in PCX format\n" ); cleanup(-1); } if (PCXH->Zenc == 0) { fprintf(stderr, "Unencoded PCX format not yet supported. Please" ); fprintf(stderr, " email the uuencoded image\n to erueg@cfgauss." ); fprintf(stderr, "uni-math.gwdg.de\n" ); cleanup(-1); } if (colors == 2) /* Allocate appropriate pbm array */ image = newBitImage ( xmax, ymax ); else { image = newRGBImage ( xmax, ymax, 8 ); } PCX_LoadImage ( zf, image ); if (colors > 16) { /* Handle external colormap */ while ((i = zgetc(zf)) != PCX_MAPSTART); clen = colors * 3; cmap = (PCXcolor *) lmalloc ( clen ); if (zread ( zf, cmap, clen ) != clen) { fprintf(stderr, "EOF while reading colormap" ); cleanup(-1); } for ( i = 0; i < colors; i++) { *(image->rgb.red + i) = (cmap [i].Zred << 8); *(image->rgb.green + i) = (cmap [i].Zgreen << 8); *(image->rgb.blue + i) = (cmap [i].Zblue << 8); } image->rgb.used = colors; lfree ( cmap ); } else if (colors > 2) { /* Handle internal colormap */ for ( i = 0; i < colors; i++) { *(image->rgb.red + i) = (PCXH->Zcmap [i].Zred << 8); *(image->rgb.green + i) = (PCXH->Zcmap [i].Zgreen << 8); *(image->rgb.blue + i) = (PCXH->Zcmap [i].Zblue << 8); } image->rgb.used = colors; } zclose ( zf ); lfree ( PCXH ); image->title = dupString(name); return ( image ); }