This file contains examples of PCX header structures and how to read, write, display, decode, and encode a PCX file and to manipulate DCX files. PCX Header File PCX.H /****************************************************************************\ ** Title: PCX.H ** ** Purpose: PCX/DCX Header file ** ** Version: 1.0 ** ** Date: April 1991 ** ** Author: James D. Murray, Anaheim, CA, USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** This file contains the header structures for the PCX and DCX image file ** ** formats. Also included are the prototypes for the functions in the ** ** source files PCXHEAD.C, DCXHEAD.C, PCXCODE.C, PCX2DCX.C, and DCX2PCX.C. ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #ifndef _PCX_H #define _PCX_H 1 #include "datatype.h" /* Include data type definitions */ /* ** The PCX header format. */ typedef struct _PcxHeader /* Offset Description */ { BYTE Id; /* 00h Manufacturer ID */ BYTE Version; /* 01h Version */ BYTE Format; /* 02h Encoding Scheme */ BYTE BitsPixelPlane; /* 03h Bits/Pixel/Plane */ WORD Xmin; /* 04h X Start (upper left) */ WORD Ymin; /* 06h Y Start (top) */ WORD Xmax; /* 08h X End (lower right) */ WORD Ymax; /* 0Ah Y End (bottom) */ WORD Hdpi; /* 0Ch Horizontal Resolution */ WORD Vdpi; /* 0Eh Vertical Resolution */ BYTE EgaPalette[48]; /* 10h 16-Color EGA Palette */ BYTE Reserved; /* 40h Reserved */ BYTE NumberOfPlanes; /* 41h Number of Color Planes */ WORD BytesLinePlane; /* 42h Bytes/Line/Plane */ WORD PaletteInfo; /* 44h Palette Interpretation */ WORD HScreenSize; /* 46h Horizontal Screen Size */ WORD VScreenSize; /* 48h Vertical Screen Size */ BYTE Filler[54]; /* 4Ah Reserved */ } PCXHEADER; /* ** PCX VGA palette. */ typedef struct _PcxVgaPalette { BYTE VgaPalette[768]; /* 256 VGA Color Palette */ } PCXVGAPALETTE; /* ** The DCX header format. */ typedef struct _DcxHeader /* Offset Description */ { DWORD Id; /* 0h File ID */ DWORD PageList[1024]; /* 4h Page List Array */ } DCXHEADER; /* ** Function prototypes */ SHORT PcxDecodeScanLine(BYTE *, WORD, FILE *); WORD PcxEncodeScanLine(BYTE *, BYTE *, WORD); VOID ReadPcxHeader(PCXHEADER *, FILE *); VOID WritePcxHeader(PCXHEADER *, FILE *); #endif /* _PCX_H */ } PCX File Header Reader PCXREAD.C This file header reader uses the ENDIANIO functions to insure that the PCX header information is always read using the Intel byte format regardless of the machine the code is running on. It also insures that the data read will be stored in the header structure properly despite any byte-alignment restrictions on the computer hardware. /****************************************************************************\ ** Title: PCXREAD ** ** Purpose: Read PCX header information from a file. ** ** Version: 1.0 ** ** Date: May 1991 ** ** Author: James D. Murray, Anaheim, CA USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** Read the header information contained within a PCX-format file and ** ** store it in a PCXHEADER structure. ** ** ** ** This module contains the following functions: ** ** ** ** ReadPcxHeader - Read a PCX header to a structure ** ** ** ** Copyright (C) 1991 Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include "endianio.h" #include "pcx.h" WORD (*GetWord)(FILE *); /* Pointer to ENDIANIO function */ /* ** Read the information from a PCX image file header. ** ** PCX header file information is read fromn the supplied FILE ** stream and written to the supplied PCX header structure. ** The ENDIANIO I/O finctions are used to insure that the header ** information is written in little-endian order. ** ** Returns: Nothing. */ VOID ReadPcxHeader(PcxHead, FpPcx) PCXHEADER *PcxHead; /* Pointer to PCX header structure */ FILE *FpPcx; /* PCX image file input FILE stream */ { register SHORT i; GetWord = GetLittleWord; /* Initialize ENDIANIO read function */ PcxHead->Id = GetByte(FpPcx); PcxHead->Version = GetByte(FpPcx); PcxHead->Format = GetByte(FpPcx); PcxHead->BitsPixelPlane = GetByte(FpPcx); PcxHead->Xmin = GetWord(FpPcx); PcxHead->Ymin = GetWord(FpPcx); PcxHead->Xmax = GetWord(FpPcx); PcxHead->Ymax = GetWord(FpPcx); PcxHead->Hdpi = GetWord(FpPcx); PcxHead->Vdpi = GetWord(FpPcx); /* Read the EGA Palette */ for (i = 0; i < sizeof(PcxHead->EgaPalette); i++) PcxHead->EgaPalette[i] = GetByte(FpPcx); PcxHead->Reserved = GetByte(FpPcx); PcxHead->NumberOfPlanes = GetByte(FpPcx); PcxHead->BytesLinePlane = GetWord(FpPcx); PcxHead->PaletteInfo = GetWord(FpPcx); PcxHead->HScreenSize = GetWord(FpPcx); PcxHead->VScreenSize = GetWord(FpPcx); /* Read the reserved area at the end of the header */ for (i = 0; i < sizeof(PcxHead->Filler); i++) PcxHead->Filler[i] = GetByte(FpPcx); } } PCX File Header Writer PCXWRITE.C The header writer is identical to the PCX file header, except that the corresponding ENDIANIO output functions are used instead. This code is necessary for safely writing PCX file header information on almost any machine. /****************************************************************************\ ** Title: PCXWRITE.C ** ** Purpose: Write PCX header information to a file. ** ** Version: 1.0 ** ** Date: May 1991 ** ** Author: James D. Murray, Anaheim, CA USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** Write the information contained within a PCXHEADER structure to a file. ** ** ** ** This module contains the following functions: ** ** ** ** WritePcxHeader - Write a PCX header to a file stream ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include "endianio.h" #include "pcx.h" VOID (*PutWord)(WORD, FILE *); /* Pointer to ENDIANIO function */ /* ** Write the information from a PCX image file structure. ** ** PCX header file information is read from the supplied PCX ** header structure and written to the supplied FILE stream. ** The ENDIANIO I/O finctions are used to insure that the header ** information is written in little-endian order. ** ** Returns: Nothing. */ VOID WritePcxHeader(PcxHead, FpPcx) PCXHEADER *PcxHead; /* Pointer to PCX header structure */ FILE *FpPcx; /* PCX image file output FILE stream */ { register SHORT i; PutWord = PutLittleWord; /* Initialize ENDIANIO write function */ PutByte(PcxHead->Id, FpPcx); PutByte(PcxHead->Version, FpPcx); PutByte(PcxHead->Format, FpPcx); PutByte(PcxHead->BitsPixelPlane, FpPcx); PutWord(PcxHead->Xmin, FpPcx); PutWord(PcxHead->Ymin, FpPcx); PutWord(PcxHead->Xmax, FpPcx); PutWord(PcxHead->Ymax, FpPcx); PutWord(PcxHead->Hdpi, FpPcx); PutWord(PcxHead->Vdpi, FpPcx); /* Write the EGA Palette */ for (i = 0; i < sizeof(PcxHead->EgaPalette); i++) PutByte(PcxHead->EgaPalette[i], FpPcx); PutByte(PcxHead->Reserved, FpPcx); PutByte(PcxHead->NumberOfPlanes, FpPcx); PutWord(PcxHead->BytesLinePlane, FpPcx); PutWord(PcxHead->PaletteInfo, FpPcx); PutWord(PcxHead->HScreenSize, FpPcx); PutWord(PcxHead->VScreenSize, FpPcx); /* Write the reserved area at the end of the header */ for (i = 0; i < sizeof(PcxHead->Filler); i++) PutByte(PcxHead->Filler[i], FpPcx); } } PCX File Information Lister PCXHEAD.C This listing program shows the information contained within a PCX file header and VGA color palette, if one is present in the image. It is a useful way of learning how to extract such information from a PCX file and a darn handy tool is you do a lot of work with PCX files in general. /****************************************************************************\ ** Title: PCXHEAD.C ** ** Purpose: Display the data in a PCX image file header. ** ** Version: 1.0 ** ** Date: April 1991 ** ** Author: James D. Murray, Anaheim, CA, USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** PCXHEAD displays all real information contained within the header of a ** ** PCX image file. The EGA color palette (map) is always displayed ** ** because this color palette always exists in the PCX header even if it ** ** if it is all zeros. If a VGA color palette exists then it is also ** ** displayed. ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include #include #include #include #include "endianio.h" #include "pcx.h" #define VERSION "1.00" int main(argc, argv) int argc; /* Number of command line arguments */ char *argv[]; /* Array of command line arguments */ { WORD i, j; /* Loop Counters */ LONG imageSize; /* Size of the PCX image file */ CHAR pcxFileName[81]; /* Holder for the PCX image file name */ FILE *fpPcx; /* File pointer to the PCX image file */ PCXHEADER pcxHead; /* PCX image file header structure */ PCXVGAPALETTE pcxVgaPal; /* PCX image file VGA palette structure */ printf("PCXHEAD - Display the header info within a PCX image file (v%s)\n", VERSION); /* Check for proper number of command line arguments */ if (argc < 2) { fputs("Usage: PCXHEAD filename.pcx\n\n", stderr); exit(-1); } /* Add the .pcx extension to the file name if no extension exists */ strncpy(pcxFileName, argv[1], 76); if (!strrchr(pcxFileName, '.')) strcat(pcxFileName, ".pcx"); /* Open the PCX image file */ if ((fpPcx = fopen(pcxFileName, "rb")) == (FILE *) NULL) { fprintf(stderr, "PCXHEAD: Cannot open file %s\n", pcxFileName); exit(-2); } /* Read the PCX image file header information */ ReadPcxHeader(&pcxHead, fpPcx); /* Check for FILE stream error */ if (ferror(fpPcx)) { fputs("PCXHEAD: Error reading header information!\n", stderr); exit(-3); } /* Check the identification byte value */ if (pcxHead.Id != 0x0A) { fprintf(stderr, "PCXHEAD: %s is not a PCX-format file!\n", pcxFileName); exit(-4); } /* Get the size of the image file */ imageSize = filelength(fileno(fpPcx)); /* ** Display, in an orderly fashion, all the information now contained ** within the PCXHEADER structure. The following code looks like ** hell in an attempt to make the information look nice on screen. ** ** Note the Horizontal and Vertical Screen Size field values are not ** displayed as they are only present in headers of PCX files created ** by PC Paintbrush, PC Paintbrush Plus, PC Paintbrush for Windows, ** and compatable programs (Version field values 4 or 5) and are ** typically not used. */ puts(" EGA Color Palette"); puts(" Color Red Green Blue"); printf("\n PCX File: %-16s", pcxFileName); printf(" 0 %03d %03d %03d\n", pcxHead.EgaPalette[0], pcxHead.EgaPalette[1], pcxHead.EgaPalette[2]); printf(" Size of Image File: %-16ld", imageSize); printf(" 1 %03d %03d %03d\n", pcxHead.EgaPalette[3], pcxHead.EgaPalette[4], pcxHead.EgaPalette[5]); printf(" Version: "); switch((int) pcxHead.Version) { case 0: printf("%-16s", "2.5"); break; case 1: printf("%-16s", "2.8"); break; case 3: printf("%-16s", "2.8 (no palette)"); break; case 5: printf("%-16s", "3.0"); break; default: printf("%-16s", "Unknown"); break; } printf(" 2 %03d %03d %03d\n", pcxHead.EgaPalette[6], pcxHead.EgaPalette[7], pcxHead.EgaPalette[8]); printf(" Encoding: %-16s", pcxHead.Format == 1 ? "RLE" : (pcxHead.Format == 0 ? "None" : "Unknown")); printf(" 3 %03d %03d %03d\n", pcxHead.EgaPalette[9], pcxHead.EgaPalette[10], pcxHead.EgaPalette[11]); printf(" Bits per Pixel: %-16d", pcxHead.BitsPixelPlane); printf(" 4 %03d %03d %03d\n", pcxHead.EgaPalette[12], pcxHead.EgaPalette[13], pcxHead.EgaPalette[14]); printf(" X Start: %-16d", pcxHead.Xmin); printf(" 5 %03d %03d %03d\n", pcxHead.EgaPalette[15], pcxHead.EgaPalette[16], pcxHead.EgaPalette[17]); printf(" Y Start: %-16d", pcxHead.Ymin); printf(" 6 %03d %03d %03d\n", pcxHead.EgaPalette[18], pcxHead.EgaPalette[19], pcxHead.EgaPalette[20]); printf(" X End: %-16d", pcxHead.Xmax); printf(" 7 %03d %03d %03d\n", pcxHead.EgaPalette[21], pcxHead.EgaPalette[22], pcxHead.EgaPalette[23]); printf(" Y End: %-16d", pcxHead.Ymax); printf(" 8 %03d %03d %03d\n", pcxHead.EgaPalette[24], pcxHead.EgaPalette[25], pcxHead.EgaPalette[26]); printf("Horizontal Resolution: %-16d", pcxHead.Hdpi); printf(" 9 %03d %03d %03d\n", pcxHead.EgaPalette[27], pcxHead.EgaPalette[28], pcxHead.EgaPalette[29]); printf(" Vertical Resolution: %-16d", pcxHead.Vdpi); printf(" 10 %03d %03d %03d\n", pcxHead.EgaPalette[30], pcxHead.EgaPalette[31], pcxHead.EgaPalette[32]); printf(" Reserved Byte: %-16d", pcxHead.Reserved); printf(" 11 %03d %03d %03d\n", pcxHead.EgaPalette[33], pcxHead.EgaPalette[34], pcxHead.EgaPalette[35]); printf(" Number of Bit Planes: %-16d", pcxHead.NumberOfPlanes); printf(" 12 %03d %03d %03d\n", pcxHead.EgaPalette[36], pcxHead.EgaPalette[37], pcxHead.EgaPalette[38]); printf(" Bytes per Scan Line: %-16d", pcxHead.BytesLinePlane * pcxHead.Numb erOfPlanes); printf(" 13 %03d %03d %03d\n", pcxHead.EgaPalette[39], pcxHead.EgaPalette[40], pcxHead.EgaPalette[41]); printf(" Palette Type: %-16s", (pcxHead.PaletteInfo == 1 ? "Color or B&W" : (pcxHead.PaletteInfo == 2 ? "Gray Scale" : "Unknown"))); printf(" 14 %03d %03d %03d\n", pcxHead.EgaPalette[42], pcxHead.EgaPalette[43], pcxHead.EgaPalette[44]); printf(" Max Number of Colors: %-16ld", 1L << (pcxHead.NumberOfPlanes * pcxH ead.BitsPixelPlane)); printf(" 15 %03d %03d %03d\n", pcxHead.EgaPalette[45], pcxHead.EgaPalette[46], pcxHead.EgaPalette[47]); /* Check for the presence of a VGA color map */ if (pcxHead.Version == 5) { fseek(fpPcx, -769L, SEEK_END); /* Seek to the VGA Id byte */ if (GetByte(fpPcx) == 0x0C) /* Is it the proper value? */ { fputs("\nHit Enter for VGA palette information...", stdout); getch(); /* Read VGA palette information from PCX file */ for (i = 0; i < sizeof(pcxVgaPal); i++) pcxVgaPal.VgaPalette[i] = GetByte(fpPcx); if (ferror(fpPcx)) fputs("PCXHEAD: Error reading VGA palette information!\n", stder r); else { puts("\n\n VGA Color Palette\n"); puts("Color Red Grn Blu Color Red Grn Blu Color Red Grn Blu Color Red Grn Blu"); /* Display the palette values */ for (i = 0, j = 0; i < 256; i += 4, j += 12) { printf(" %3d %03u %03u %03u %3d %03u %03u %03u", i, pcxVgaPal.VgaPalette[j], pcxVgaPal.VgaPalette[j+1], pcxVgaPal.VgaPalette[j+2], i+1, pcxVgaPal.VgaPalette[j+3], pcxVgaPal.VgaPalette[j+4], pcxVgaPal.VgaPalette[j+5]); printf(" %3d %03u %03u %03u %3d %03u %03u %03u\n", i+2, pcxVgaPal.VgaPalette[j+6], pcxVgaPal.VgaPalette[j+7], pcxVgaPal.VgaPalette[j+8], i+3, pcxVgaPal.VgaPalette[j+9], pcxVgaPal.VgaPalette[j+10], pcxVgaPal.VgaPalette[j+11]); if (i && i % 88 == 0) { fputs("Hit Enter for next page...", stdout); getch(); puts("\n\nColor Red Grn Blu Color Red Grn Blu \ Color Red Grn Blu Color Red Grn Blu"); } } putchar('\n'); } } else puts("\nPCXHEAD: No VGA color palette found."); } fclose(fpPcx); return(0); /* Successful termination */ } } PCX Run-length Decoder PCXDECOD.C PCX decoders need to be aware of image data that contains runs which start at the end of one scan line and finish at the start of the next line. This function decodes one scan line at a time and will keep track of runs if they extend to the next scan line. /****************************************************************************\ ** Title: PCXDECODE.C ** ** Purpose: Decode a PCX image file scan line. ** ** Version: 1.0 ** ** Date: May 1991 ** ** Author: James D. Murray, Anaheim, CA USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** Decode an image scan line using the PCX run-length encoding algorithm ** ** Decoded data is returned in a buffer. Useful for algorithms that need ** ** to work on images one scan line at a time. ** ** ** ** This module contains the following functions: ** ** ** ** PcxDecodeScanLine - Decode a PCX RLE-encoded scan line ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include "endianio.h" #include "pcx.h" /* ** Decode (uncompress) a PCX image file scan line. ** ** PcxDecodeScanLine() decodes a buffer containing scan line data encoded ** using the PCX run-length encoding algorithm. The encoded data is read ** from a FILE stream, decoded, and then written to a pointer to a buffer ** passed to this function. ** ** The PCX specification states (in so many words) that the run-length ** encoding of a pixel run should stop at the end of each scan line. ** However, some PCX encoders may continue the encoding of a pixel run on ** to the beginning of the next scan line, if possible. This code, ** therefore, assumes that any pixel run can span scan lines. ** ** To check for decoding errors, the value returned by PcxDecodeScanLine() ** should be the same as the value of BufferSize (the length of an ** uncompressed scan line). ** ** Returns: Total number of pixels decoded from compressed scan line, ** or EOF if a file stream error occured. */ SHORT PcxDecodeScanLine(DecodedBuffer, BufferSize, FpPcx) BYTE *DecodedBuffer; /* Pointer to buffer to hold decoded data */ WORD BufferSize; /* Size of buffer to hold decoded data */ FILE *FpPcx; /* FILE pointer to the open input PCX image file */ { WORD index = 0; /* Index into compressed scan line buffer */ SHORT total = 0; /* Running total of decoded pixel values */ BYTE byte; /* Data byte read from PCX file */ static BYTE runcount = 0; /* Length of decoded pixel run */ static BYTE runvalue = 0; /* Value of decoded pixel run */ /* ** If there is any data left over from the previous scan ** line write it to the beginning of this scan line. */ do { /* Write the pixel run to the buffer */ for (total += runcount; /* Update total */ runcount && index < BufferSize; /* Don't read past buffer */ runcount--, index++) DecodedBuffer[index] = runvalue; /* Assign value to buffer */ if (runcount) /* Encoded run ran past end of scan line */ { total -= runcount; /* Subtract count not written to buffer */ return(total); /* Return number of pixels decoded */ } /* ** Get the next encoded run packet. ** ** Read a byte of data. If the two MBSs are 1 then this byte ** holds a count value (0 to 63) and the following byte is the ** data value to be repeated. If the two MSBs are 0 then the ** count is one and the byte is the data value itself. */ byte = GetByte(FpPcx); /* Get next byte */ if ((byte & 0xC0) == 0xC0) /* Two-byte code */ { runcount = byte & 0x3F; /* Get run count */ runvalue = GetByte(FpPcx); /* Get pixel value */ } else /* One byte code */ { runcount = 1; /* Run count is one */ runvalue = byte; /* Pixel value */ } } while (index < BufferSize); /* Read until the end of the buffer */ if (ferror(FpPcx)) return(EOF); /* File stream error. Probably a premature EOF */ return(total); /* Return number of pixels decoded */ } } PCX Run-length Encoder PCXENCOD.C Run-length encoding is as easy as decoding. This function encodes a single scan line of data at a time and will not encode across scan lines. /****************************************************************************\ ** Title: PCXENCOD.C ** ** Purpose: Encode a PCX image file scan line. ** ** Version: 1.0 ** ** Date: May 1991 ** ** Author: James D. Murray, Anaheim, CA USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** Compress an image scan line using the PCX run-length encoding ** ** algorithm. Useful for algorithms that need to work on images one scan ** ** line at a time. ** ** ** ** This module contains the following functions: ** ** ** ** PcxEncodeScanLine - Encode a raw scan line using PCX RLE. ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include "endianio.h" #include "pcx.h" /* ** Encode (compress) a PCX image file scan line. ** ** PcxEncodeScanLine() encodes a buffer containing raw scan line data ** using the PCX PackBytes run-length encoding algorithm. The encoded ** data is written to a second buffer passed to the function. ** ** The main problem when encoding a scan line is that we never know how ** long the resulting encoded scan line will be. Typically it will be ** shorter than the unencoded line, however, this type of RLE encoding ** can make the resulting encoded data larger in size than the unencoded ** data depending upon the data being encoded. ** ** It is therefore important that the buffer used to hold the encoded ** scan line be larger than what will typically be needed. A size of ** 2048 bytes should be more than plenty for typical VGA images. The ** size of the buffer can be trimmed down to the size of the encoded ** data using realloc() and the return value of PcxEncodeScanLine() ** function, if necessary. ** ** Returns: Number of bytes in compressed scan line. */ WORD PcxEncodeScanLine(DecodedBuffer, EncodedBuffer, BufferSize) BYTE *DecodedBuffer; /* Pointer to buffer holding unencoded data */ BYTE *EncodedBuffer; /* Pointer to buffer to hold encodeded scan line */ WORD BufferSize; /* Size of buffer holding unencoded data */ { register WORD index = 0; /* Index into uncompressed data buffer */ register WORD scanindex = 0; /* Index into compressed data buffer */ BYTE runcount; /* Length of encoded pixel run */ BYTE runvalue; /* Value of encoded pixel run */ while (index < BufferSize) { /* ** Get the run count of the next pixel value run. ** ** Pixel value runs are encoded until a different pixel value ** is encountered, the end of the scan line is reached, or 63 ** pixel values have been counted. */ for (runcount = 1, runvalue = DecodedBuffer[index]; runvalue == DecodedBuffer[index + runcount] && index + runcount < BufferSize && runcount < 63; runcount++); /* ** Encode the run into a one or two-byte code. ** ** Multiple pixel runs are stored in two-byte codes. If a single ** pixel run has a value of less than 64 then it is stored in a ** one-byte code. If a single pixel run has a value of 64 to 255 ** then it is stored in a two-byte code. */ if (runcount > 1) /* Multiple pixel run */ { EncodedBuffer[scanindex++] = runcount | 0xC0; EncodedBuffer[scanindex++] = runvalue; } else /* Single pixel run */ { if (DecodedBuffer[index] < 64) /* Value is 0 to 63 */ { EncodedBuffer[scanindex++] = runvalue; } else /* Value is 64 to 255 */ { EncodedBuffer[scanindex++] = runcount | 0xC0; EncodedBuffer[scanindex++] = runvalue; } } index += runcount; /* Jump ahead to next pixel run value */ } return(scanindex); /* Return the number of bytes written to buffer */ } } PCX File Reader and Writer PCXCODE.C This program uses all the functions in the previous listings to create a program that reads, decodes, encodes, and writes a PCX image file. This program is only an example of how to implement the PCX functions in your own code. However, if you have an PCX files that appear partly munged when displayed, you might try running them through this encoder/decoder to see if it can fix them. /****************************************************************************\ ** Title: PCXCODE ** ** Purpose: Decode/Encode a PCX image file. ** ** Version: 1.0 ** ** Date: May 1991 ** ** Author: James D. Murray, Anaheim, CA USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** This code is an example of several functions that work with PCX image ** ** files. PCXCODE reads a PCX image file, decodes the image, data ** ** re-encodes it, and writes it out to a second PCX file. While this ** ** program is only a working example, it can be used to possibly repair ** ** PCX files that have been previously encoded incorrectly and fail to ** ** display properly. ** ** ** ** Copyright (C) 1991 Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include #include #include "endianio.h" #include "pcx.h" int main(argc, argv) int argc; /* Number of command line arguments */ char *argv[]; /* Array of command line arguments */ { register WORD i; /* Loop counter */ FILE *fpPcxIn; /* PCX image file input FILE stream */ FILE *fpPcxOut; /* PCX image file output FILE stream */ PCXHEADER pcxHead; /* PCX image file header structure */ WORD numOfColors; /* Number of colors in the image */ WORD xSize; /* Width of image in pixels */ WORD ySize; /* Height of image in scan lines */ SHORT scanLineLength; /* Length of uncompressed scan line */ BYTE *decodedBuffer; /* Buffer holding decoded scan line */ BYTE *encodedBuffer; /* Buffer holding encoded scan line */ WORD scanLineSize; /* Size of encoded scan line */ SHORT ret; /* Return value holder */ puts("PCXCODE - Decode and recode a PCX image file (v1.00)\n"); /* Check for proper number of command line arguments */ if (argc < 3) { fputs("Usage: PCXHEAD input_filename.pcx output_filename.pcx\n\n", stderr); exit(-1); } /* Open the input PCX image file */ if ((fpPcxIn = fopen(argv[1], "rb")) == (FILE *) NULL) { fprintf(stderr, "PCXHEAD: Cannot open input file %s\n", argv[1]); exit(-2); } /* Open the output PCX image file */ if ((fpPcxOut = fopen(argv[2], "wb")) == (FILE *) NULL) { fprintf(stderr, "PCXHEAD: Cannot open output file %s\n", argv[2]); exit(-3); } /* Read the PCX image file header information */ ReadPcxHeader(&pcxHead, fpPcxIn); /* Check for FILE stream error */ if (ferror(fpPcxIn)) { fputs("PCXHEAD: Error reading header information!\n", stderr); exit(-4); } /* Check the identification byte value */ if (pcxHead.Id != 0x0A) { fprintf(stderr, "PCXHEAD: Not a PCX-format file!\n"); exit(-5); } /* Calculate size of image in pixels and scan lines */ xSize = pcxHead.Xmax - pcxHead.Xmin + 1; ySize = pcxHead.Ymax - pcxHead.Ymin + 1; /* Do some more calculations */ numOfColors = (1 << (pcxHead.BitsPixelPlane * pcxHead.NumberOfPlanes)); scanLineLength = pcxHead.NumberOfPlanes * pcxHead.BytesLinePlane; /* Display some PCX image file information */ printf("Image Width = %3d\nImage Length = %3d\n", xSize, ySize); printf("Number of Colors: %d\n", numOfColors); printf("Size of uncompressed scan line: %d\n\n", scanLineLength); /* Write out the PCX image file header */ WritePcxHeader(&pcxHead, fpPcxOut); /* Create an uncompressed scanline buffer */ if ((decodedBuffer = (BYTE *) malloc(scanLineLength)) == NULL) { fputs("PCXCODE: Cannot allocate scan line buffer.", stderr); exit(-6); } /* Create a compressed scanline buffer */ if ((encodedBuffer = (BYTE *) malloc(2048)) == NULL) { fputs("PCXCODE: Cannot allocate scan line buffer.", stderr); exit(-7); } /* ** Read an encoded scan line, decode it, reencode it, and write it to ** the output file stream. Continue until all scan lines are read. */ for (i = 0; i < ySize; i++) { printf("Scan Line %03d\r", i); /* Read scan line and decode it */ if ((ret = PcxDecodeScanLine(decodedBuffer, scanLineLength, fpPcxIn)) != scanLineLength) { printf("PCXCODE: Bad scan line length (was %d should be %d)\n", ret, scanLineLength); } /* Encode scan line */ scanLineSize = PcxEncodeScanLine(decodedBuffer, encodedBuffer, scanLineLength); /* Write scan line to output FILE stream */ fwrite((BYTE *) encodedBuffer, sizeof(BYTE), scanLineSize, fpPcxOut); } /* If there is a VGA palette then read and append it to the output file */ if (pcxHead.Version == 5) { fseek(fpPcxIn, -769L, SEEK_END); /* Seek backwards from end of file */ if (GetByte(fpPcxIn) == 0x0C) /* VGA Palette ID value? */ { PCXVGAPALETTE PcxVgaPal; /* PCX VGA palette structure */ fputc(0x0c, fpPcxOut); /* Read the VGA palette */ for (i = 0; i < sizeof(PcxVgaPal); i++) PcxVgaPal.VgaPalette[i] = GetByte(fpPcxIn); /* Write the VGA palette */ for (i = 0; i < sizeof(PcxVgaPal); i++) PutByte(PcxVgaPal.VgaPalette[i], fpPcxOut); } } fclose(fpPcxIn); fclose(fpPcxOut); return(0); } } DCX File Information Lister DCXHEAD.C This listing program prints out the number and size of each PCX file contained within a DCX file. It is a good example of how to parse the data in a DCX file header and page list. /****************************************************************************\ ** Title: DCXHEAD.C ** ** Purpose: Display the data in a DCX image file header. ** ** Version: 1.0 ** ** Date: April 1991 ** ** Author: James D. Murray, Anaheim, CA, USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** DCX2PCX extracts all the PCX images from within a DCX file. A DCX file ** ** may contain up to 1023 images. Each PCX image is a complete PCX image ** ** file, including header, palette, and compressed image data. ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include #include #include "endianio.h" #include "pcx.h" DWORD (*GetDword)(FILE *); /* Pointer to ENDIANIO function */ int main(argc, argv) int argc; char *argv[]; { WORD counter; /* Number of PCX images in DCX file */ FILE *fpDcx; /* File pointer to the DCX image file */ CHAR dcxFileName[81]; /* Holder for the DCX image file name */ DCXHEADER dcxHead; /* DCX image file header structure */ puts("DCXHEAD - Display the header info within a DCX image file (v1.00)\n"); /* Check for proper number of command line arguments */ if (argc < 2) { fputs("Usage: DCXHEAD filename.dcx\n\n", stderr); exit(-1); } /* Add the .dcx extension to the file name if no extension exists */ strncpy(dcxFileName, argv[1], 80); if (!strrchr(argv[1], '.')) strcat(dcxFileName, ".dcx"); /* Open the DCX image file */ if ((fpDcx = fopen(dcxFileName, "rb")) == NULL) { fprintf(stderr, "DCXHEAD: Cannot open file %s\n", dcxFileName); exit(-2); } /* Read using little-endian byte order */ GetDword = GetLittleDword; /* Read the ID word */ dcxHead.Id = GetDword(fpDcx); /* Check the identification word value */ if (dcxHead.Id != 987654321L) { fprintf(stderr, "DCXHEAD: %s is not a DCX-format file!\n", dcxFileName); exit(-3); } printf("Magic number: %ld\n\n", dcxHead.Id); /* Zero-out the page list */ memset(&dcxHead.PageList, 0, sizeof(dcxHead.PageList)); /* Read the page list one entry at a time */ for (counter = 0; dcxHead.PageList[counter] = GetDword(fpDcx); counter++) printf("PCX Image %d Offset: %ld\n", counter + 1, dcxHead.PageList[counter]); printf("\nTotal Number of PCX images in DCX file %s: %u\n", dcxFileName, counter); /* Check for FILE stream errors */ if (ferror(fpDcx)) { fputs("DCXHEAD: Error reading header information!\n", stderr); exit(-3); } return(0); /* Successful termination */ } } PCX to DCX Image File Converter PCX2DCX.C Although this program is called a converter, it is really an archiver, in that it takes a bunch of PCX image files and makes one big DCX image file out of them without really changing the PCX files themselves. It is included as an example of how to use the PCX and DCX code together in a single application. /***************************************************************************\ ** Title: PCX2DCX.C ** ** Purpose: Append or more PCX image files to a DCX file. ** ** Version: 1.0 ** ** Date: April 1991 ** ** Author: James D. Murray, Anaheim, CA, USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** PCX2DCX will append one or more PCX files to an existing DCX image ** ** file. If the DCX file named does not exist it is then created by ** ** PCX2DCX. A DCX file may contain no more than 1023 PCX image pages. ** ** Wildcards are supported. ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include #include #include #include #include "endianio.h" #include "pcx.h" DWORD (*GetDword)(FILE *); #ifdef __TURBOC__ #include #define FindFirst(p, f, a) findfirst(p, f, a) #define FindNext(f) findnext(f) #define FindStruct ffblk #define FindName ff_name #define FindSize ff_fsize #else /* MSC */ #define FindFirst(p, f, a) _dos_findfirst(p, a, f) #define FindNext(f) _dos_findnext(f) #define FindStruct find_t #define FindName name #define FindSize size #endif #define BUFFERSIZE 8192 /* Size of the copy buffer */ #define TEMPFILENAME "PCX2DCX.TMP" /* Temporary file name */ #define SAVEFILENAME "SAVEFILE.DCX" /* DCX save file name */ int main(argc, argv) int argc; char *argv[]; { register WORD i; /* Loop counter */ FILE *fpDcx; /* DCX file pointer */ FILE *fpPcx; /* PCX file pointer */ FILE *fpTmp; /* Temporary file pointer */ CHAR *ptr; /* Pointer used by strrchr() */ WORD pageCount; /* Count of PCX pages in DCX file */ WORD appendCount = 0; /* Count of PCX pages appended to DCX file */ WORD retsize; /* fread() return size value holder */ LONG offsetCount; /* Current offset in temporary file */ CHAR pcxFileName[20]; /* PCX file name holder */ CHAR dcxFileName[20]; /* DCX file name holder */ CHAR buffer[BUFFERSIZE]; /* Copy buffer */ DCXHEADER dcxHead; /* DCX header structure */ struct FindStruct fileinfo; /* File information block structure */ puts("PCX2DCX - Convert PCX image file(s) to DCX image file (v1.00)\n\n"); /* Check for proper number of command line arguments */ if (argc < 3) { fputs("Usage: PCX2DCX filename.DCX filename.PCX [ ... ]", stderr); exit(-1); } strcpy(dcxFileName, argv[1]); if (!strrchr(dcxFileName, '.')) /* Check if the file name has an */ strcat(dcxFileName, ".DCX"); /* extension and append .DCX if not */ /* Initialize (zero-out) the page list */ memset(dcxHead.PageList, 0, sizeof(dcxHead.PageList)); /* Open (create) the temporary file */ if ((fpTmp = fopen(TEMPFILENAME, "wb+")) == NULL) { fprintf(stderr, "PCX2DCX: Cannot create file %s\n", TEMPFILENAME); exit(-2); } /* Read using little-endian byte order */ GetDword = GetLittleDword; /* Zero-out page list */ memset(&dcxHead.PageList, 0, sizeof(dcxHead.PageList)); if (!access(dcxFileName, 00)) { /* If DCX file does exist... */ printf("Appending to DCX image file %s\n", dcxFileName); /* Open the DCX image file */ if ((fpDcx = fopen(dcxFileName, "rb+")) == NULL) { fprintf(stderr, "PCX2DCX: Cannot open file %s\n", dcxFileName); exit(-2); } /* Read the ID word */ dcxHead.Id = GetDword(fpDcx); /* Check the magic number */ if (dcxHead.Id != 987654321L) { fprintf(stderr, "PCX2DCX: %s is not a DCX-format file!\n", dcxFileName); exit(-3); } /* ** Load the page table with the offset values in the DCX file and ** count the number of PCX image pages in the DCX file. In order ** to save space it is possible that some software will create DCX ** files that do not have a full 4096-byte page table. PCX2DCX, ** therefore, does not assume that all data from bytes 4 to 4099 ** in any DCX image file is a PCX image page offset value. */ i = 0; do { dcxHead.PageList[i] = GetDword(fpDcx); } while (dcxHead.PageList[i++] != (DWORD) 0); pageCount = --i; /* Seek to first PCX image page */ fseek(fpDcx, dcxHead.PageList[0], SEEK_SET); /* Copy all PCX image pages to the temporary file */ while ((retsize = fread(&buffer, sizeof(char), BUFFERSIZE, fpDcx)) == sizeof(buffer)) fwrite(&buffer, sizeof(char), retsize, fpTmp); fwrite(&buffer, sizeof(char), retsize, fpTmp); offsetCount = ftell(fpDcx); /* Get next offset */ fclose(fpDcx); /* Save the original DCX file, just in case... */ if (rename(dcxFileName, SAVEFILENAME) == -1) fputs("PCX2DCX: Rename failed. Continuing...", stderr); } else /* else, DCX file does not exist... */ { printf("Creating DCX image file %s\n", dcxFileName); dcxHead.Id = 987654321L; /* Set the magic number */ offsetCount = 4100L; /* First PCX page starts at offset 4100 */ pageCount = 0; /* Number of PCX pages added to DCX file */ } /* Open (create) the DCX file */ if ((fpDcx = fopen(dcxFileName, "wb")) == NULL) { fprintf(stderr, "PCX2DCX: Cannot create file %s\n", dcxFileName); exit(-4); } /* For each PCX file specification in the argument list... */ for (i = 2; argv[i]; i++) { if (FindFirst(argv[i], &fileinfo, 0) == -1) break; do { /* Add the path to the PCX file name if one was given */ strcpy(pcxFileName, fileinfo.FindName); if ((ptr = strrchr(argv[i], '\\')) != NULL) { *ptr = '\0'; strcpy(pcxFileName, argv[i]); *ptr = '\\'; strcat(pcxFileName, fileinfo.FindName); } /* Open the PCX image file */ if ((fpPcx = fopen(pcxFileName, "rb")) == NULL) { fprintf(stderr, "PCX2DCX: Cannot create file %s\n", TEMPFILENAME); exit(-2); } printf("\nCopying file %-13s Size = %ld", pcxFileName, fileinfo.FindSize); /* Copy the PCX image file to the temporary file */ while ((retsize = fread(&buffer, sizeof(char), BUFFERSIZE, fpPcx)) == sizeof(buffer)) fwrite(&buffer, sizeof(char), retsize, fpTmp); fwrite(&buffer, sizeof(char), retsize, fpTmp); fclose(fpPcx); /* Close the PCX image file */ /* Write offset of PCX page to the page list */ dcxHead.PageList[pageCount] = offsetCount; /* Add PCX page size to DCX offset */ offsetCount += fileinfo.FindSize; /* Increment page and append counters */ pageCount++; appendCount++; /* Check if the page table is full */ if (pageCount > 1023) break; } while(FindNext(&fileinfo) == 0); /* Check if the page table is full */ if (pageCount > 1023) { fputs("\nPCX2DCX: Maximum number of PCX files reached.", stderr); break; } } /* Write the DCX header to the DCX image file */ fwrite(&dcxHead, sizeof(char), sizeof(dcxHead), fpDcx); fflush(fpTmp); /* Flush the buffer stream */ rewind(fpTmp); /* Set file pointer to byte 0 */ /* Copy the temporary file if PCX image pages to the DCX image file */ while ((retsize = fread(&buffer, sizeof(char), BUFFERSIZE, fpTmp)) == sizeof(buffer)) fwrite(&buffer, sizeof(char), retsize, fpDcx); fwrite(&buffer, sizeof(char), retsize, fpDcx); fclose(fpDcx); /* Close the DCX file */ fclose(fpTmp); /* Close the temporary file */ unlink(TEMPFILENAME); /* Delete the temporary file */ unlink(SAVEFILENAME); /* Delete the DCX save file */ printf("\n\n%d PCX image files appended to DCX image file %s \ (%d images total).\n\n", appendCount, dcxFileName, pageCount); return(0); /* Successful termination */ } } DCX to PCX Image File Converter DCX2PCX Once you have made a DCX file why not split it back up into its PCX file components? Once again, this program is just one example of an application that can be written using the information and code in this directory and in its corresponding documentation. /****************************************************************************\ ** Title: DCX2PCX.C ** ** Purpose: Extract PCX image files from a DCX image file. ** ** Version: 1.0 ** ** Date: April 1991 ** ** Author: James D. Murray, Anaheim, CA USA ** ** C Compilers: Borland C++ v2.0, Microsoft C v6.00a ** ** ** ** DCX2PCX extracts all the PCX images from within a DCX file. A DCX file ** ** may contain up to 1023 PCX images. Each PCX image is a complete PCX ** ** image file, including header, palette, and compressed image data. ** ** ** ** Copyright (C) 1991 by Graphics Software Labs. All rights reserved. ** \****************************************************************************/ #include #include #include #include "endianio.h" #include "pcx.h" DWORD (*GetDword)(FILE *); /* Pointer to ENDIANIO function */ #define BUFFERSIZE 8192 /* Size of copy buffer */ int main(argc, argv) int argc; char *argv[]; { register WORD i, j; /* Loop counters */ FILE *fpDcx; /* DCX file input file stream */ FILE *fpPcx; /* PCX file output file stream */ int counter = 0; /* Counter used to construct PCX file names */ int retsize; /* fread() return size value holder */ int numberOfBlockReads; /* Number of block to fread() in DCX file */ int remainder; /* Remainder of data to fread() */ long lpcxFileSize; /* Size of a PCX file */ char pcxFileName[20]; /* PCX file name holder */ char dcxFileName[20]; /* DCX file name holder */ char buffer[BUFFERSIZE]; /* Copy buffer */ DCXHEADER dcxHead; /* DCX header structure */ puts("DCX2PCX - Extract PCX image file(s) from a DCX file (v1.00)\n\n"); /* Check for proper number of command line arguments */ if (argc < 2) { fputs("Usage: DCX2PCX filename.DCX", stderr); exit(-1); } strcpy(dcxFileName, argv[1]); if (!strrchr(dcxFileName, '.')) /* Check if the file name has an */ strcat(dcxFileName, ".DCX"); /* extension and append .DCX if not */ /* Open the DCX image file */ if ((fpDcx = fopen(dcxFileName, "rb")) == NULL) { fprintf(stderr, "DCX2PCX: Cannot open file %s\n", dcxFileName); exit(-2); } /* Read using little-endian byte order */ GetDword = GetLittleDword; /* Read the ID word */ dcxHead.Id = GetDword(fpDcx); /* Check the magic number */ if (dcxHead.Id != 987654321L) { fprintf(stderr, "DCX2PCX: %s is not a DCX-format file!\n", dcxFileName); exit(-3); } /* Zero-out page list */ memset(&dcxHead.PageList, 0, sizeof(dcxHead.PageList)); /* Read the page list from the DCX file */ i = 0; do { dcxHead.PageList[i] = GetDword(fpDcx); } while (dcxHead.PageList[i++] != (DWORD) 0); /* For each PCX image in the DCX file ... */ for (i = 0; dcxHead.PageList[i]; i++) { /* ** The DCX format does not allow a way to preserve the orignal ** file names of the PCX images, so we must make new file names. ** ** Names may range from PCX0000.PCX to PCX1022.PCX for a total ** of 1023 PCX image files. */ sprintf(pcxFileName, "PCX%04d.PCX", counter); if ((fpPcx = fopen(pcxFileName, "wb")) == NULL) { fprintf(stderr, "DCX2PCX: Cannot create PCX file %s\n", pcxFileName); break; } printf("Extracting PCX image file %-13s", pcxFileName); /* Seek to the next PCX image */ fseek(fpDcx, dcxHead.PageList[i], SEEK_SET); /* If this next PCX image is not the last PCX image in the file ... */ if (dcxHead.PageList[i + 1] != 0) { /* ** Calculate the number of blocks in a PCX image based on ** the value of the BUFFERSIZE identifier. */ lpcxFileSize = dcxHead.PageList[i+1] - dcxHead.PageList[i]; numberOfBlockReads = (int) (lpcxFileSize / (long) BUFFERSIZE); remainder = (int) (lpcxFileSize % (long) BUFFERSIZE); printf(" Size = %ld\n", lpcxFileSize); /* Read and write blocks of data */ for (j = 0; j < numberOfBlockReads; j++) { if ((retsize = fread(&buffer, sizeof(char), BUFFERSIZE, fpDcx)) == sizeof(buffer)) fwrite(&buffer, sizeof(char), BUFFERSIZE, fpPcx); } fread(&buffer, sizeof(char), remainder, fpDcx); fwrite(&buffer, sizeof(char), remainder, fpPcx); /* Check if file pointer is where its suppose to be */ if (ftell(fpDcx) != dcxHead.PageList[i + 1]) fprintf(stderr, "DCX2PCX: File position error!\ (are: %ld should be: %ld)\n", ftell(fpDcx), dcxHead.PageList[i + 1]); } else /* Extract last PCX image in the DCX file */ { while ((retsize = fread(buffer, sizeof(char), BUFFERSIZE, fpDcx)) == sizeof(buffer)) fwrite(&buffer, sizeof(char), BUFFERSIZE, fpPcx); fwrite(&buffer, sizeof(char), retsize, fpPcx); printf(" Size = %ld\n", ftell(fpDcx) - dcxHead.PageList[i]); } fclose(fpPcx); /* Close the PCX file */ counter++; /* Increment the PCX file counter */ } fclose(fpDcx); /* Close the DCX file */ printf("\n%d PCX image files extracted.\n\n", counter); return(0); /* Successful termination */ } }