/** ** $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $ ** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC) ** a division of General Atomics, San Diego, California, USA ** ** Users and possessors of this source code are hereby granted a ** nonexclusive, royalty-free copyright and design patent license to ** use this code in individual software. License is not granted for ** commercial resale, in whole or in part, without prior written ** permission from SDSC. This source is provided "AS IS" without express ** or implied warranty of any kind. ** ** For further information contact: ** E-Mail: info@sds.sdsc.edu ** ** Surface Mail: Information Center ** San Diego Supercomputer Center ** P.O. Box 85608 ** San Diego, CA 92138-5608 ** (619) 534-5000 **/ #define HEADER " $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $" /** ** FILE ** impcx.c - ZSoft IBM PC Paintbrush image file I/O ** ** PROJECT ** libim - SDSC image manipulation library ** ** DESCRIPTION ** impcx.c contains routines to read and write ZSoft PCX files for ** the image manipulation library. Raster data read in is stored ** in a VFB. Raster data written out is taken from a tag table. ** ** PUBLIC CONTENTS ** d =defined constant ** f =function ** m =defined macro ** t =typedef/struct/union ** v =variable ** ? =other ** ** imPcxRead f read an PCX file ** imPcxWrite1 f write a 1-bit PCX file ** imPcxWrite8 f write an 8-bit PCX file ** ** PRIVATE CONTENTS ** imPcxHeaderInfo t header for PCX files ** imPcxHeaderFields v header description for binary I/O package ** ** HISTORY ** $Log: /roq/libim/impcx.c $ * * 1 11/02/99 4:38p Zaphod ** Revision 1.12 1995/06/29 00:28:04 bduggan ** updated copyright year ** ** Revision 1.11 1995/06/15 20:48:58 bduggan ** Removed useless ++. Changed bzero to memset. ** ** Revision 1.10 1995/04/03 21:32:34 bduggan ** took out #ifdef NEWMAGIC ** ** Revision 1.9 1995/01/10 23:36:33 bduggan ** put in IMMULTI, IMPIPE instead of TRUE/FALSE ** uncapitlized i's in local functions ** made read/write routines static ** ** Revision 1.8 94/10/03 11:30:21 nadeau ** Updated to ANSI C and C++ compatibility. ** Removed all use of register keyword. ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) ** Changed all float arguments to double. ** Added forward declarations. ** Added misc. casts to passify SGI and DEC compilers. ** Changed all macros and defined constants to have names ** starting with IM. ** Rearranged magic number structures for format handlers. ** Made format handler routines static (i.e., local to file). ** Updated comments, adding format descriptions and references. ** Updated indenting on some code. ** Updated copyright message. ** ** Revision 1.7 92/12/03 01:50:23 nadeau ** Corrected info messages. ** ** Revision 1.6 92/11/23 18:42:43 nadeau ** Removed use of IMINFOMSG. ** ** Revision 1.5 92/11/04 12:12:37 groening ** put ImFIleFormat info and magic number info ** from imfmt.c into this file. ** ** Revision 1.4 92/09/29 18:00:29 vle ** Added ImInfo messages. ** ** Revision 1.3 92/08/31 17:30:11 vle ** Updated copyright notice. ** ** Revision 1.2 91/10/03 09:15:18 nadeau ** Added undocumented PC PaintBrush CLT type. ** ** Revision 1.1 91/09/17 20:16:28 nadeau ** Initial revision ** **/ #include "iminternal.h" /** ** $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $ ** Copyright (c) 1989-1994 San Diego Supercomputer Center (SDSC) ** a division of General Atomics, San Diego, California, USA ** ** Users and possessors of this source code are hereby granted a ** nonexclusive, royalty-free copyright and design patent license to ** use this code in individual software. License is not granted for ** commercial resale, in whole or in part, without prior written ** permission from SDSC. This source is provided "AS IS" without express ** or implied warranty of any kind. ** ** For further information contact: ** E-Mail: info@sds.sdsc.edu ** ** Surface Mail: Information Center ** San Diego Supercomputer Center ** P.O. Box 85608 ** San Diego, CA 92138-5608 ** (619) 534-5000 **/ #define HEADER " $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $" /** ** FILE ** impcx.c - ZSoft IBM PC Paintbrush image file I/O ** ** PROJECT ** libim - SDSC image manipulation library ** ** DESCRIPTION ** impcx.c contains routines to read and write ZSoft PCX files for ** the image manipulation library. Raster data read in is stored ** in a VFB. Raster data written out is taken from a tag table. ** ** PUBLIC CONTENTS ** d =defined constant ** f =function ** m =defined macro ** t =typedef/struct/union ** v =variable ** ? =other ** ** done ** ** PRIVATE CONTENTS ** imPcxRead f read an PCX file ** imPcxWrite1 f write a 1-bit PCX file ** imPcxWrite8 f write an 8-bit PCX file ** imPcxHeaderInfo t header for PCX files ** imPcxHeaderFields v header description for binary I/O package ** ** HISTORY ** $Log: /roq/libim/impcx.c $ * * 1 11/02/99 4:38p Zaphod ** Revision 1.12 1995/06/29 00:28:04 bduggan ** updated copyright year ** ** Revision 1.11 1995/06/15 20:48:58 bduggan ** Removed useless ++. Changed bzero to memset. ** ** Revision 1.10 1995/04/03 21:32:34 bduggan ** took out #ifdef NEWMAGIC ** ** Revision 1.9 1995/01/10 23:36:33 bduggan ** put in IMMULTI, IMPIPE instead of TRUE/FALSE ** uncapitlized i's in local functions ** made read/write routines static ** ** Revision 1.8 94/10/03 11:30:21 nadeau ** Updated to ANSI C and C++ compatibility. ** Removed all use of register keyword. ** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char) ** Changed all float arguments to double. ** Added forward declarations. ** Added misc. casts to passify SGI and DEC compilers. ** Changed all macros and defined constants to have names ** starting with IM. ** Rearranged magic number structures for format handlers. ** Made format handler routines static (i.e., local to file). ** Updated comments, adding format descriptions and references. ** Updated indenting on some code. ** Updated copyright message. ** ** Revision 1.7 92/12/03 01:50:23 nadeau ** Corrected info messages. ** ** Revision 1.6 92/11/23 18:42:43 nadeau ** Removed use of IMINFOMSG. ** ** Revision 1.5 92/11/04 12:12:37 groening ** put ImFIleFormat info and magic number info ** from imfmt.c into this file. ** ** Revision 1.4 92/09/29 18:00:29 vle ** Added ImInfo messages. ** ** Revision 1.3 92/08/31 17:30:11 vle ** Updated copyright notice. ** ** Revision 1.2 91/10/03 09:15:18 nadeau ** Added undocumented PC PaintBrush CLT type. ** ** Revision 1.1 91/09/17 20:16:28 nadeau ** Initial revision ** **/ #include "iminternal.h" /** ** FORMAT ** PCX - ZSoft IBM PC Paintbrush image ** ** AKA ** pcc ** ** FORMAT REFERENCES ** - Technical Reference Manual for PC Paintbrush et al, ZSoft Corp. ** - Bit-Mapped Graphics, Steve Rimmer ** - Supercharged Bitmapped Graphics, Steve Rimmer ** - Graphics File Formats, David C. Kay, John R. Levine ** - HP LaserJet Programming, Andrew Binstock, David Babcock, Marv Luse ** ** CODE CREDITS ** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991. ** ** DESCRIPTION ** ZSoft's PCX format is used by a variety of IBM PC software, including ** ZSoft's own Publisher's Paintbrush, PC Paintbrush Plus, and PC ** Paintbrush. ** ** This code supports the format as described in: ** ** Technical Reference Manual ** ZSoft Corporation ** 450 Franklin Rd. Suite 100 ** Marietta, GA 30067 ** (404) 428-0008 ** ** This report is dated 1988. ** ** Additional format information was obtained from the book "Bit-Mapped ** Graphics" by Steve Rimmer and published by WINDCREST/McGraw-Hill. ** ** PCX files start with a fixed-length 128-byte header consisting of ** the following: ** ** Byte Item Size Description/Comments ** ** 0 Manufacturer 1 10 = ZSoft .PCX ** ** 1 Version 1 Version # ** 0 = Version 2.5 ** 2 = Version 2.8 w/palette info ** 3 = Version 2.8 wo/palette info ** 5 = Version 3.0 ** ** 2 Encoding 1 1 = .PCX run length encoding ** ** 3 Bits per pixel 1 # of bits/pixel per plane ** 1 = monochrome (CGA) ** 4 = 16-color (EGA) ** 8 = 256-color (VGA) ** ** 4 Window 4*2 Picture dimensions ** xmin ** ymin ** xmax ** ymax ** ** 12 Horizontal Res 2 Horizontal resolution of dev, in pixels ** ** 14 Vertical Res 2 Vertical resolution of dev, in pixels ** ** 16 Colormap 16*3 Basic color table ** ** 64 Reserved 1 ** ** 65 No. Planes 1 # of color planes ** ** 66 Bytes per line 2 # of bytes per scanline per color ** color plane (always even) ** ** 68 Colormap type 2 Colormap type ** 1 = color/BW ** 2 = grayscale ** ** 70 Filler 58 Blanks ** ** All 2-byte integers are given in Intel byte order (LBF). ** ** Image data immediately follows the header. In version 6.0 PCX files, ** an optional 256-entry color palette may follow the image data (but ** see below). ** ** HEADER DETAILS ** The 'Manufacturer' field is always '10' for PCX files. ZSoft has never ** introduced further 'Manufacturer' codes. The SDSC Image Tools use ** the 'Manufacturer' field as the file's magic number. ** ** The 'Encoding' field is always '1'. ZSoft has never introduced other ** encoding schemes (which it should have, since this one is particularly ** stupid). ZSoft does not support unencoded images in PCX files. ** ** The 'Window' field is used to compute the image dimensions using: ** ** width = xmax - xmin + 1; ** height = ymax - ymin + 1; ** ** Note the '+1' needed on both of these. Just a quirk of PCX files. ** On write, the SDSC Image Tools set the 'xmin' and 'ymin' fields to 0, ** and the 'xmax' and 'ymax' fields to the width and height, minus 1. ** ** The 'Horizontal Res' and 'Vertical Res' fields are irrelevant. ** SDSC Image Tools code ignores these on read, and sets them to 0's ** on write. ** ** NOTE: The popular UNIX PBM+ tools set the horizontal and ** vertical device resolution to the image size on write, but ** ignore them on read. This is incorrect. The resolution fields ** are defined by the spec to be HARDWARE resolution, not image ** resolution. We feel that setting them to zeroes is less ** incorrect than setting them to some value. ** ** The 'Reserved' and 'Filler' fields are always ignored by us on ** read, and always set to 0's on write. ** ** The remaining fields have specific combinations of values depending ** upon the type of image being stored. ** ** MONOCHROME IMAGES ** Monochrome PCX files were intended for the original CGA card as well ** as single-plane use of the EGA card. ** ** Header values set to the following flag monochrome images: ** ** Version = 0, 2, or 3 ** Bits per pixel = 1 ** Colormap = ignored ** No. Planes = 1 ** Bytes per line = (width + 7) / 8 rounded up to even byte count ** Colormap type = ignored ** ** Image data following the header is an encoded buffer with 8 pixels ** to the byte. The left-most bit in each byte corresponds to the ** left-most pixel of each group of 8. Scanline buffers are rounded up ** to an even number of bytes, then encoded (see below). ** ** "Extra" image data may follow the last scanline in order to bring the ** image data up to a multiple of 8 or 16 scanlines. On read, we ignore ** such extra data. On write we don't write extra data. ** ** 4-BIT COLOR IMAGES ** 4-bit color PCX files were intended for the four-plane mode of the ** EGA card. ** ** Header values set to the following flag 4-bit color images: ** ** Version = 2 or 3 ** Bits per pixel = 1 ** Colormap = used (see below) ** No. Planes = 4 ** Bytes per line = (width + 7)/8 rounded up to even byte count ** Colormap type = ignored ** ** If the 'Version' field of the header is a '2', the header contains ** a color palette in the 'Colormap' portion of the header. Palette ** values are given from low to high and consist of 16 3-byte RGB triplets. ** ** If the 'Version' field of the header is a '3', the header does not ** contain a palette for the image. For our purposes, this makes the ** image grayscale. ** ** Image data following the header is grouped as four consequetive ** 1-bit scanlines of data. Each 1-bit scanline is packed into bytes ** at 8 pixels to the byte, with the left most bit in each byte ** corresponding to the left-most pixel of each group of 8. The entire ** set of 4 1-bit scanlines is considered one buffer, and as a whole ** is padded to the next higher even byte boundary, then encoded. ** ** For encoding purposes, the group of 4 1-bit scanlines is considered ** a single buffer. Encoding always breaks at the end of such a buffer, ** but might not break going from one 1-bit scanline to the next within ** such a group. ** ** "Extra" image data may follow the last scanline in order to bring the ** image data up to a multiple of 8 or 16 scanlines. We neither read it ** or write it. ** ** 8-BIT COLOR IMAGES ** 8-bit color PCX files were intended for the VGA card. ** ** Header values set to the following flag 8-bit color images: ** ** Version = 5 ** Bits per plane = 8 ** Colormap = unused (see below) ** No. Planes = 1 ** Bytes per line = ((width + 1)/2) * 2 ** Colormap type = 1 or 2 ** ** The 'Version' field will always be 5 for 8-bit color images. ** ** The color palette for 8-bit color images is too big for the 'Colormap' ** field of the header. Instead, the palette, if any, is tacked onto ** the end of the image data. The "official" procedure (from the spec) ** is to seek to 769 bytes before the end of the file and read a byte. ** If it is a 0x0C (12 decimal), then the next 768 bytes are 256 3-byte ** RGB triplets. ** ** The 'Colormap type' field is meant to toggle the VGA card between ** grayscale and color. In our case, it is meaningless. The existance ** of a palette makes the image color. Nonexistance means the image is ** grayscale. On read, we ignore this field. On write, we always set ** it to 1 (color). ** ** Image data following the header is encoded from a stream of bytes, ** at one byte per pixel. Consequetive bits in each byte correspond to ** consequetive color planes (as with normal graphics hardware). ** As with monochrome and 4-bit images, each scanline buffer is rounded ** up to an even number of bytes. Encoding breaks at the end of each ** scanline. ** ** "Extra" image data may follow the last scanline in order to bring the ** image data up to a multiple of 8 or 16 scanlines. We neither read or ** write such extra scanlines. ** ** ENCODING ** Image data buffers are encoded with a type of run-length encoding. ** A data byte with its upper two bits set is a run count for the number ** of times to repeat the next byte in the file. Data bytes without ** their upper two bits set are literal values. For example: ** ** Decode: ** pEncoded = encodedBuffer; ** pDecoded = decodedBuffer; ** pEnd = decodedBuffer + BytesPerLine; ** while ( pDecoded < pEnd ) ** { ** if ( (*pEncoded & 0xC0) == 0xC0 ) ** { ** count = (*pEncoded++) & 0x3F; ** value = *pEncoded++; ** for ( j = 0; j < count; j++ ) ** *pDecoded++ = value; ** } ** else ** *pDecoded++ = *pEncoded++; ** } ** ** When encoding one must watch for data values with their upper two ** bits set. Such bytes are encoded as a run byte of 1 followed by ** the data byte. ** ** The worst case image only contains data bytes with their upper two ** bits set. This will cause an encoded image to take DOUBLE the space ** of the unencoded image. Brain dead. ** ** COLOR TABLE CAVEATS ** #1: PBM+ Problems. ** According to the spec, the existance and style of CLT is determined ** by the header's version number: ** ** 0 2.5 no CLT ** 2 2.8W CLT in header ** 3 2.8WO no CLT ** 5 3.0 CLT at end of file (possibly) ** ** Unfortunately, the popular PBM+ package for UNIX systems always ** generates PCX files for verison 3.0, and assumes other packages ** will check the header plane and bits per pixel fields to decide ** where a CLT is. ** ** In order to be compatible with this software, we ignore the version ** number header and similarly depend upon the plane and bits per pixel ** counts to decide what type of CLT, if any, is present. The only ** version number we check is 2.8WO which explicitly says there is no ** CLT for the image and that the header CLT should be ignored. ** ** Fortunately, this change of parsing still maintains compatibility ** with PCX usage. For CLT writing, we choose the right version number ** the way we're supposed to. ** ** #2: Spec Problems. ** The PCX spec recommends finding end-of-file CLTs by the following: ** ** lseek( fd, -769, 2 ); -- seek to end of file, -769 bytes ** read( fd, buffer, 1 ); -- read 1 byte ** if ( *buffer == 0x0C ) ** there is a CLT ** ** This is not a robust way of checking for an EOF CLT. Imagine the ** case where there is no CLT, and seeking back 769 bytes gets you ** into the middle of image data. 0x0C is not a byte that is impossible ** to encounter in image data. If you happened to encounter it, you ** would mistakenly conclude there was a CLT. ** ** The popular UNIX PBM+ package simply reads the image data in, one ** byte at a time. When it has finished reading in the data, it looks ** at the next byte to see if it is 0x0C. If so, it reads in a CLT. ** ** This is neither speedy (lots of single byte reads) nor robust. ** The spec says: ** "There may be extra scan lines at the bottom of the image, ** to round to 8 or 16 scan lines." ** ** If they were there, PBM+ would not find the CLT. ** ** Our approach is more robust and much faster. ** ** The CLT is read in first by the seek approach. Doing it first is ** merely convenient algorithmically. It could be done last as well. ** ** The image data is then read in by making a worst-case guess at its ** size in bytes (twice the size of a decoded image). This will ** very likely read too much data. This is OK since we watch the ** decode count while decoding so that we don't decode off the end of ** the image. The advantage is that we can read it all in with one ** read system call. Much much much faster. ** ** After decoding, a count is returned of the number of encoded bytes ** used. To determine if the CLT we read in earlier is truely a CLT, ** and not image data improperly interpreted, we compare the file position ** of the 769-bytes-from-the-end CLT with the position we'd be at if we ** had only read in the image data (header + # of encoded bytes used). ** If the CLT file position is in advance of the end-of-image data ** position, then it is considered a valid CLT. ** ** It is still possible that there are lots and lots of "extra" scanlines ** after the image data and that they contain 0x0C's. A seek into the ** midst of these would still falsly conclude there was a CLT there. ** This seems very unlikely. Software that pads with extra scanlines ** usually sets them to zeroes. ** ** IMAGE DATA CAVEATS ** #1: Even-byte-count buffers ** According to the spec, all scanlines, prior to encoding, are rounded ** up to an even number of bytes. Unfortunately, the popular PBM+ ** package for UNIX systems fails to do this. ** ** In order to be compatible with this software, we check the header's ** "Bytes per line" field and skip the difference between it and the ** size of a decoded buffer. If the PCX generator made the byte count ** even, this will skip 1 byte for odd-length lines. If the PCX generator ** did not make this even, such as PBM+, then this will skip no bytes ** in such cases. ** ** Fortunately, this change of skipping still maintains compatibility ** with PCX usage. For image writing, we round up the way we're ** supposed to. ** ** #2: 2-bit and 3-bit color images ** According to the documentation, 2-bit and 3-bit images aren't really ** allowed. However, the poplular UNIX PBM+ package freely generates ** them. ** ** In order to be compatible with such code, we also accept 2-bit and ** 3-bit images as variations of the 4-bit case. ** ** Header values set to the following flag 4-bit color images: ** ** Version = 2 or 3 ** Bits per pixel = 1 ** Colormap = used (see below) ** No. Planes = 2 or 3 ** Bytes per line = (width + 7)/8 rounded up to even byte count ** Colormap type = ignored ** ** 2-bit and 3-bit cases are stored the same as for the 4-bit case. */ /* * PCX - ZSoft IBM PC Paintbrush * For information on these structures, how to use them, etc. please * see imfmt.c. */ #ifdef __STDC__ static int imPcxRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); static int imPcxWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); static int imPcxWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ); #else static int imPcxRead( ); static int imPcxWrite1( ), imPcxWrite8( ); #endif static char *imPcxNames[ ] = { "pcx", "pcc", NULL }; static unsigned char imPcxMagicNumber[ ] = { 0x0a }; static ImFileFormatReadMap imPcxReadMap[ ] = { /* in out */ /* type,ch,dep, attr. VFB type attr. */ { IN,1,1, RLE, IMVFBMONO, 0 }, { IN,1,2, RLE, IMVFBINDEX8, 0 }, { IN,1,2, RLE|C, IMVFBINDEX8, C }, { IN,1,3, RLE, IMVFBINDEX8, 0 }, { IN,1,3, RLE|C, IMVFBINDEX8, C }, { IN,1,4, RLE, IMVFBINDEX8, 0 }, { IN,1,4, RLE|C, IMVFBINDEX8, C }, { IN,1,8, RLE, IMVFBINDEX8, 0 }, { IN,1,8, RLE|C, IMVFBINDEX8, C }, { -1, 0, -1, 0 }, }; static ImFileFormatWriteMap imPcxWriteMap[ ] = { /* in out */ /* VFB type, attr., type,ch,dep, attr., func */ { IMVFBMONO, 0, IN,1,1, RLE, imPcxWrite1 }, { IMVFBINDEX8, 0, IN,1,8, RLE, imPcxWrite8 }, { IMVFBINDEX8, C, IN,1,8, RLE| C, imPcxWrite8 }, { -1, 0, -1, 0, NULL }, }; static ImFileMagic imFilePcxMagic[]= { { 0, 1, imPcxMagicNumber }, { 0, 0, NULL }, }; ImFileFormat ImFilePcxFormat = { imPcxNames, "ZSoft IBM PC Paintbrush file", "ZSoft Corp.", "1-bit monochrome (CGA), 2-bit, 3-bit, and 4-bit color (EGA) files\n\ with or without a color table, and 8-bit color (VGA) files with or\n\ without a color table. All are RLE-encoded.", "1-bit monochrome (CGA), and 8-bit color (VGA) files with or\n\ without a color table. All are RLE-encoded.", imFilePcxMagic, IMNOMULTI, IMNOPIPE, IMNOMULTI, IMPIPE, imPcxRead, imPcxReadMap, imPcxWriteMap }; /* * TYPEDEF & STRUCT * imPcxHeaderInfo - header for PCX files * imPcxHeaderFields - header description for binary I/O package * * DESCRIPTION * PCX files start with a header that gives various details of the * image contained in the file. */ typedef struct imPcxHeaderInfo /* PCX */ { unsigned char pcx_manufacturer; unsigned char pcx_version; unsigned char pcx_encoding; unsigned char pcx_bitsPerPixel; unsigned short pcx_window[4]; unsigned short pcx_horizontalRes; unsigned short pcx_verticalRes; unsigned char pcx_clt[16][3]; unsigned char pcx_reserved; unsigned char pcx_nPlanes; unsigned short pcx_bytesPerLine; unsigned short pcx_cltType; unsigned char pcx_filler[58]; } imPcxHeaderInfo; static BinField imPcxHeaderFields[ ] = { /* pcx_manufacturer */ UCHAR, 1, 1, /* pcx_version */ UCHAR, 1, 1, /* pcx_encoding */ UCHAR, 1, 1, /* pcx_bitsPerPixel */ UCHAR, 1, 1, /* pcx_window */ USHORT, 2, 4, /* pcx_horizontalRes */ USHORT, 2, 1, /* pcx_verticalRes */ USHORT, 2, 1, /* pcx_clt */ UCHAR, 1, 48, /* pcx_reserved */ UCHAR, 1, 1, /* pcx_nPlanes */ UCHAR, 1, 1, /* pcx_bytesPerLine */ USHORT, 2, 1, /* pcx_cltType */ USHORT, 2, 1, /* pcx_filler */ UCHAR, 1, 58, 0, 0, 0, }; #define IMPCXHEADERSIZE (128) /* * Value for fields: */ #define IMPCXMANUFACTURER 0x0A /* ZSoft .PCX */ #define IMPCXV_2_5 0 /* Version 2.5 */ #define IMPCXV_2_8W 2 /* Version 2.8 w/clt */ #define IMPCXV_2_8WO 3 /* Version 2.8 wo/clt */ #define IMPCXV_3_0 5 /* Version 3.0 */ #define IMPCXENCODING 1 /* Run-length encoded */ #define IMPCXCOLOR 1 /* Color or BW CLT */ #define IMPCXGRAY 2 /* Grayscale CLT */ #define IMPCXPAINTBRUSH 2992 /* Strange PC Paintbrush type */ /* * FUNCTION * imPcxDecode - decode a run buffer * * DESCRIPTION * An incomming encoded data buffer is decoded into a second buffer. */ static int /* Returns # of bytes decoded */ #ifdef __STDC__ imPcxDecode( unsigned char *src, int nDst, unsigned char *dst ) #else imPcxDecode( src, nDst, dst ) unsigned char *src; /* Source buffer (encoded) */ int nDst; /* # of bytes in destination buffer*/ unsigned char *dst; /* Destination buffer (decoded) */ #endif { int i; /* Counter */ unsigned char value; /* Run value */ unsigned char *dstLast; /* Last byte +1 of dst buffer */ unsigned char *srcSave; /* Saved src pointer */ srcSave = src; dstLast = dst + nDst; while ( dst < dstLast ) { if ( (*src & 0xC0) != 0xC0 ) *dst++ = *src++; else { i = *src++ & 0x3F; for ( value = *src++; i; i-- ) *dst++ = value; } } return ( src - srcSave ); } /* * FUNCTION * imPcxEncode - encode a run buffer * * DESCRIPTION * An incomming data buffer is encoded into a second buffer. Bytes * with their upper two bits set are flagged and stored as runs of 1. * * Note: becuase the upper two bits of a byte flag a run, we get two * special cases to watch for: * 1. The maximum run length is 63 (lower 6 bits set). * 2. Data bytes with the upper two bits set have to be * treated as runs of 1. */ static int /* Returns # of bytes encoded */ #ifdef __STDC__ imPcxEncode( unsigned char *src, int nSrc, unsigned char *dst ) #else imPcxEncode( src, nSrc, dst ) unsigned char *src; /* Source buffer (encoded) */ int nSrc; /* # of bytes in source buffer */ unsigned char *dst; /* Destination buffer (decoded) */ #endif { int run; /* Run count */ unsigned char *srcLast; /* Last byte +1 of source buffer*/ unsigned char *dstSave; /* Saved original dst value */ srcLast = src + nSrc - 1; dstSave = dst; while ( src <= srcLast ) { run = 0; while ( *src == *(src+1) && src < srcLast && run < 62 ) { run++; src++; } if ( run > 0 ) *dst++ = (run + 1) | 0xC0;/* Store the run count*/ else if ( (*src & 0xC0) == 0xC0 ) *dst++ = 1 | 0xC0; /* Make it a run of 1 */ *dst++ = *src++; } return ( dst - dstSave ); } /* * FUNCTION * imPcxRead - read a PCX file * * DESCRIPTION * The file header is read and checked. The color table, if any, is * read in. The image data is read in and decoded, then dispersed into * a VFB of the appropriate depth. The VFB and CLT are added to the * tag table. */ static int /* Returns # tags read in */ #ifdef __STDC__ imPcxRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) #else imPcxRead( ioType, fd, fp, flagsTable, tagTable ) int ioType; /* I/O flags */ int fd; /* Input file descriptor */ FILE *fp; /* Input file pointer */ TagTable *flagsTable; /* Flags */ TagTable *tagTable; /* Tag table to add to */ #endif { ImVfb *vfb; /* Read in image */ ImVfbPtr pPixel; /* Pcxel pointer */ ImClt *clt; /* Image CLT */ ImCltPtr pColor; /* Color pointer */ unsigned char cltBuffer[769]; /* VGA card palette */ unsigned char *pCltBuffer; /* Buffer pointer */ unsigned char *encoded; /* Encoded scanline buffer */ unsigned char *decoded; /* Decoded scanline buffer */ unsigned char *pDecoded; /* Decoded buffer pointer */ unsigned char value; /* Pixel value */ unsigned char value2; /* Old pixel value */ imPcxHeaderInfo header; /* PCX file header */ int height, width; /* Image dimensions */ int width8; /* Width / 8 */ int widthextra; /* Width % 8 */ int n; /* Byte count */ int i, j; /* Counter */ int x, y; /* Current scanline */ int cltInHeader; /* Get the CLT from the header */ int cltAtEnd; /* Get the CLT from the EOF */ long cltFilePosition; /* File position of CLT */ long imageFilePosition; /* File position of Image */ char message[100]; /* ImInfo message */ /* * Read in the header in LBF (Intel) byte order. Check that it is * valid. */ BinByteOrder( BINLBF ); if ( ImBinReadStruct( ioType, fd, fp, &header, imPcxHeaderFields )== -1) ImReturnBinError( ); if ( header.pcx_manufacturer != IMPCXMANUFACTURER ) ImErrorFatal( "Bad manufacturer code in PCX file header", -1, IMESYNTAX ); switch ( header.pcx_version ) { case IMPCXV_2_5: ImInfo( "Version", "2.5" ); break; case IMPCXV_2_8W: ImInfo( "Version", "2.8 with Palette" ); break; case IMPCXV_2_8WO: ImInfo( "Version", "2.8 without Palette" ); break; case IMPCXV_3_0: ImInfo( "Version", "3.0" ); break; default: ImErrorFatal( "Unknown file version in PCX file header", -1, IMESYNTAX ); } if ( header.pcx_encoding != IMPCXENCODING ) ImErrorFatal( "Unknown encoding method in PCX file header", -1, IMESYNTAX ); switch ( header.pcx_bitsPerPixel ) { case 1: /* 1-bit monochrome (CGA) */ /* 2-bit color (EGA) */ /* 3-bit color (EGA) */ /* 4-bit color (EGA) */ if ( header.pcx_nPlanes > 4 ) ImErrorFatal( "Bad color plane count in PCX file header", -1, IMESYNTAX ); if ( header.pcx_version == IMPCXV_2_8WO ) cltInHeader = FALSE; else cltInHeader = TRUE; cltAtEnd = FALSE; break; case 8: /* 8-bit color (VGA) */ if ( header.pcx_nPlanes != 1 ) ImErrorFatal( "Bad color plane count in PCX file header", -1, IMESYNTAX ); cltInHeader = FALSE; cltAtEnd = TRUE; break; default: ImErrorFatal( "Unknown image depth. Must be 1, 4, or 8.", -1, IMESYNTAX ); } width = header.pcx_window[2] - header.pcx_window[0] + 1; height = header.pcx_window[3] - header.pcx_window[1] + 1; switch ( header.pcx_cltType ) { case 0: /* For backwards compatibility */ case IMPCXCOLOR: case IMPCXGRAY: case IMPCXPAINTBRUSH: break; default: ImErrorFatal( "Unknown palette type in PCX file header", -1, IMESYNTAX ); } ImInfo( "Byte Order", "Least Significant Byte First" ); sprintf( message, "%d x %d", width, height ); ImInfo( "Resolution", message ); sprintf( message, "%d-bit Color Indexed", header.pcx_bitsPerPixel); ImInfo( "Type", message ); /* * Make a CLT if we need one. */ clt = IMCLTNULL; if ( cltInHeader ) { /* 16-color CLT included in header. */ if ( (clt = ImCltAlloc( 16 )) == IMCLTNULL ) ImErrorFatal( ImQError( ), -1, ImErrNo ); pColor = ImCltQFirst( clt ); for ( i = 0; i < 16; i++ ) { ImCltSRed( pColor, header.pcx_clt[i][0] ); ImCltSGreen( pColor, header.pcx_clt[i][1] ); ImCltSBlue( pColor, header.pcx_clt[i][2] ); ImCltSInc( clt, pColor ); } ImInfo( "Color Table", "16 Entries" ); } else if ( cltAtEnd ) { /* * Seek to 769 bytes from the bottom and see if there * is a 0x0C there. If so, we've got a 256 entry palette. * * HOWEVER, there is the chance that seeking to 769 bytes * from the end will put us within the image data if there * really isn't a CLT. If that next image data byte just * happened to be a 0x0C, then we'd falsely conclude there * was a CLT. This is a problem in the PCX spec. * * To handle this case, we remember where things are and * later compare where we got the alleged CLT versus the * end of the image data. If it is too early in the file, * we were fooled and we get rid of the bogus CLT. */ imageFilePosition = ImTell( ioType, fd, fp ); ImSeek( ioType, fd, fp, -769, 2 ); cltFilePosition = ImTell( ioType, fd, fp ); n = ImBinRead( ioType, fd, fp, cltBuffer, UCHAR, 1, 769 ); if ( n != -1 && *cltBuffer == 0x0C ) { if ( (clt = ImCltAlloc( 256 )) == IMCLTNULL ) ImErrorFatal( ImQError( ), -1, ImErrNo ); pColor = ImCltQFirst( clt ); pCltBuffer = cltBuffer + 1; for ( i = 0; i < 256; i++ ) { ImCltSRed( pColor, *pCltBuffer++ ); ImCltSGreen( pColor, *pCltBuffer++ ); ImCltSBlue( pColor, *pCltBuffer++ ); ImCltSInc( clt, pColor ); } ImInfo( "Color Table", "256 Entries" ); } else { ImInfo( "Color Table", "none" ); } ImSeek( ioType,fd, fp, IMPCXHEADERSIZE, 0 ); } ImInfo( "Compression Type", "Run Length Encoded (RLE)" ); /* * Read in the image data. Unfortunately, because it is encoded, * we don't know how much data there is. We do know our worst * case encoding could produce a file twice the size of the image. * So, we allocate a buffer that big and try to read that much * data. It is very likely that BinRead will return less than * that. Fine. We then decode that buffer into a new buffer * that is the size of the image (in bytes). */ n = header.pcx_bytesPerLine * header.pcx_nPlanes * height; ImMalloc( encoded, unsigned char *, 2 * n ); ImMalloc( decoded, unsigned char *, n ); if ( ImBinRead( ioType, fd, fp, encoded, UCHAR, 1, 2 * n ) == -1 ) { free( (char *)encoded ); free( (char *)decoded ); if ( clt != IMCLTNULL ) ImCltFree( clt ); ImReturnBinError( ); } n = imPcxDecode( encoded, n, decoded ); /* * If the location at which we found the CLT is within the image * data we just read in, then there wasn't really a CLT there at * all. It just happened that seeking to 769 bytes from the end * fell upon a 0x0C within the image data, and we falsly concluded * there was a CLT. We fix our mistake now. */ if ( cltAtEnd && imageFilePosition + n > cltFilePosition ) { ImCltFree( clt ); clt = IMCLTNULL; } /* * Walk the decoded buffer and expand it into a new VFB. */ if ( header.pcx_bitsPerPixel == 1 && header.pcx_nPlanes == 1 ) { /* * Monochrome. The decoded buffer contains packed bytes, * each holding 8 1-bit pixel values. Watch for widths that * are not a multiple of 8, and remember to toss an extra * byte at the end of each scanline if the width (in bytes) * is not even. */ if ( (vfb=ImVfbAlloc( width, height, IMVFBMONO )) == IMVFBNULL ) { free( (char *)encoded ); free( (char *)decoded ); if ( clt != IMCLTNULL ) ImCltFree( clt ); ImErrorFatal( ImQError( ), -1, ImErrNo ); } pPixel = ImVfbQFirst( vfb ); pDecoded = decoded; width8 = width / 8; /* Width in bytes*/ widthextra = width & 0xF; /* width % 8 */ for ( y = 0; y < height; y++ ) { for ( x = 0; x < width8; x++ ) { value = *pDecoded++; for ( i = 7; i >= 0; i-- ) { ImVfbSMono( vfb, pPixel, (value>>i) & 0x1 ); ImVfbSInc( vfb, pPixel ); } } if ( widthextra ) { value = *pDecoded++; for ( i = 7; i >= (8-widthextra); i-- ) { ImVfbSMono( vfb, pPixel, (value>>i) & 0x1 ); ImVfbSInc( vfb, pPixel ); } } /* * According to the spec, we only have to skip a * byte if the width is odd. However, some packages * don't adhear to the even-byte-count requirement * (such as PBM+). So, we only skip if the header * says we should, and then as much as it says we * should. */ for ( i = width8 + (widthextra?1:0); i < header.pcx_bytesPerLine; i++ ) pDecoded++; /* Skip */ } } else if ( header.pcx_bitsPerPixel == 1 ) { /* * n-bit color (usually 4-bit). The decoded buffer contains * packed bytes, each holding 8 1-bit pixel values... yes, * 1-bit pixel values! Each scanline is stored as nPlanes * of 1-bit planes. We need to remember to toss an extra * byte per set if each scanline plane set's byte count is * not even. */ if ( (vfb=ImVfbAlloc( width, height, IMVFBINDEX8 ))==IMVFBNULL ) { free( (char *)encoded ); free( (char *)decoded ); if ( clt != IMCLTNULL ) ImCltFree( clt ); ImErrorFatal( ImQError( ), -1, ImErrNo ); } pDecoded = decoded; width8 = width / 8; /* Width in bytes*/ widthextra = width & 0xF; /* width % 8 */ for ( y = 0; y < height; y++ ) { /* First plane. */ pPixel = ImVfbQPtr( vfb, 0, y ); for ( x = 0; x < width8; x++ ) { value = *pDecoded++; for ( i = 7; i >= 0; i-- ) { ImVfbSIndex8( vfb, pPixel, ((value>>i) & 0x1) ); ImVfbSInc( vfb, pPixel ); } } if ( widthextra ) { value = *pDecoded++; for ( i = 7; i >= (8-widthextra); i-- ) { ImVfbSIndex8( vfb, pPixel, ((value>>i) & 0x1) ); ImVfbSInc( vfb, pPixel ); } } /* Additional planes. */ for ( j = 1; j < header.pcx_nPlanes; j++ ) { pPixel = ImVfbQPtr( vfb, 0, y ); for ( x = 0; x < width8; x++ ) { value = *pDecoded++; for ( i = 7; i >= 0; i-- ) { value2 = ImVfbQIndex8( vfb, pPixel ); ImVfbSIndex8( vfb, pPixel, value2 | ((value>>i) & 0x1)<= (8-widthextra); i-- ) { value2 = ImVfbQIndex8( vfb, pPixel ); ImVfbSIndex8( vfb, pPixel, value2 | ((value>>i) & 0x1)<= 0; i-- ) { value |= (ImVfbQMono( vfb, pPixel ) & 0x1) << i; ImVfbSInc( vfb, pPixel ); } *pDecoded++ = value; } if ( widthextra ) { value = 0; for ( i = 7; i >= (8-widthextra); i-- ) { value |= (ImVfbQMono( vfb, pPixel ) & 0x1) << i; ImVfbSInc( vfb, pPixel ); } *pDecoded++ = value; } /* * Encoded it and write it out. */ n = imPcxEncode( decoded, header.pcx_bytesPerLine, encoded ); if ( ImBinWrite( ioType, fd, fp, encoded, UCHAR, 1, n ) == -1 ) { free( (char *)encoded ); free( (char *)decoded ); ImReturnBinError( ); } } free( (char *)encoded ); free( (char *)decoded ); return ( 1 ); } /* * FUNCTION * imPcxWrite8 - write an 8-bit PCX file * * DESCRIPTION * The PCX file header set up and written out followed by the encoded * image data. */ static int /* Returns # of tags used */ #ifdef __STDC__ imPcxWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable ) #else imPcxWrite8( pMap, ioType, fd, fp, flagsTable, tagTable ) ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */ int ioType; /* I/O flags */ int fd; /* Input file descriptor */ FILE *fp; /* Input file pointer */ TagTable *flagsTable; /* Flags */ TagTable *tagTable; /* Tag table to read from */ #endif { ImVfb *vfb; /* Read in image */ ImVfbPtr pPixel; /* Pixel pointer */ ImClt *clt; /* Color lookup table */ ImCltPtr pColor; /* Color pointer */ imPcxHeaderInfo header; /* PCX file header */ unsigned char *encoded; /* Encoded data buffer */ unsigned char *decoded; /* Decoded data buffer */ unsigned char *pDecoded; /* Encoded buffer pointer */ unsigned char *colorBuf; /* Color table output buffer */ unsigned char *pColorBuf; /* Color table buffer pointer */ int n; /* Byte count */ int i, j; /* Color counters */ int x, y; /* Pixel counters */ int height, width; /* Image size */ char message[100]; /* ImInfo message */ /* * Set up the header and write it out. */ TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb ); clt = ImVfbQClt( vfb ); width = ImVfbQWidth( vfb ); height = ImVfbQHeight( vfb ); BinByteOrder( BINLBF ); memset( (void *)&header, 0x00, sizeof( header ) ); header.pcx_manufacturer = IMPCXMANUFACTURER; header.pcx_version = IMPCXV_3_0; /* VGA color */ header.pcx_encoding = IMPCXENCODING; header.pcx_bitsPerPixel = 8; /* 8-bit color */ header.pcx_window[0] = 0; /* xmin */ header.pcx_window[1] = 0; /* ymin */ header.pcx_window[2] = width - 1; /* xmax (minus 1) */ header.pcx_window[3] = height - 1; /* ymax (minus 1) */ header.pcx_nPlanes = 1; /* 1 color plane */ header.pcx_bytesPerLine = width; if ( header.pcx_bytesPerLine & 0x1 ) ++header.pcx_bytesPerLine; /* Make it even */ header.pcx_cltType = IMPCXCOLOR; /* Yup, its color */ /* Remaining fields left zero. */ if ( ImBinWriteStruct( ioType, fd, fp, &header, imPcxHeaderFields )==-1) { ImReturnBinError( ); } /* * Output -verbose message */ ImInfo( "Version", "3.0" ); ImInfo( "Byte Order", "Least Significant Byte First" ); sprintf( message, "%d x %d", width, height ); ImInfo( "Resolution", message); ImInfo( "Type", "8-bit Color Indexed" ); ImInfo( "Color Table", "256 Entries" ); ImInfo( "Compression Type", "Run Length Encoded (RLE)" ); /* * Buffer up, encode, and write out the image data. Remember that * an encoded buffer could take up twice as much space as the * decoded one, in the pathelogical case. */ ImMalloc( decoded, unsigned char *, header.pcx_bytesPerLine ); ImMalloc( encoded, unsigned char *, 2 * header.pcx_bytesPerLine ); memset( (void *)decoded, 0x00, header.pcx_bytesPerLine ); pPixel = ImVfbQFirst( vfb ); for ( y = 0; y < height; y++ ) { pDecoded = decoded; for ( x = 0; x < width; x++ ) { *pDecoded++ = ImVfbQIndex8( vfb, pPixel ) & 0xFF; ImVfbSInc( vfb, pPixel ); } /* * Encoded it and write it out. */ n = imPcxEncode( decoded, header.pcx_bytesPerLine, encoded ); if ( ImBinWrite( ioType, fd, fp, encoded, UCHAR, 1, n ) == -1 ) { free( (char *)encoded ); free( (char *)decoded ); ImReturnBinError( ); } } free( (char *)encoded ); free( (char *)decoded ); /* * Write out the color table (if any). Remember to start off the * color table with the special "yes it's here" byte: 0x0C. */ if ( clt == IMCLTNULL ) return ( 1 ); /* No color table */ n = 256 * 3 + 1; /* Always 256 entries */ ImMalloc( colorBuf, unsigned char *, n ); memset( (void *)colorBuf, 0x00, n ); pColorBuf = colorBuf; *pColorBuf++ = 0x0C; /* Yes, it's here */ /* Fill in with CLT entries from the VFB. */ i = ImCltQNColors( clt ); pColor = ImCltQFirst( clt ); for ( j = 0; j < i; j++ ) { *pColorBuf++ = ImCltQRed( pColor ) & 0xFF; *pColorBuf++ = ImCltQGreen( pColor ) & 0xFF; *pColorBuf++ = ImCltQBlue( pColor ) & 0xFF; ImCltSInc( clt, pColor ); } /* Pad to 256 entries. */ for ( ; j < 256; j++ ) { *pColorBuf++ = 0; *pColorBuf++ = 0; *pColorBuf++ = 0; } /* Write it out. */ if ( ImBinWrite( ioType, fd, fp, colorBuf, UCHAR, 1, n ) == -1 ) { free( (char *)colorBuf ); ImReturnBinError( ); } free( (char *)colorBuf ); return ( 2 ); /* VFB + CLT */ }