ZSOFT PICTURE EXCHANGE FILE FORMAT (.PCX) PCX (PiCture eXchange) is an image file format developed by ZSoft Corporation for use on IBM-compatible PCs (and thus uses little endian byte ordering). It is the native file format for PC Paintbrush and FRIEZE package by the same manufacturer. PCX files contain only one image (usually palette-indexed) and its associated palette information. The encoding scheme used for PCX encoding is a simple (yet efficient) RLE algorithm with a 2-bit denote. 1.0 FILE STRUCTURE PCX files are structured as follows: HEADER required 128 bytes ENCODED IMAGE DATA required variable-length EXTENDED COLOR MAP MARKER optional 1 byte EXTENDED COLOR MAP optional 768 bytes The EXTENDED COLOR MAP MARKER and the EXTENDED COLOR MAP are associated (if one is present, the other must follow). These two blocks may only exist if the header field Version is 5. 2.0 HEADER PCX files always start with a 128-byte long header structured as follows: UINT8 Manufacturer; // Must be 10 for ZSoft PCX. UINT8 Version; // Version code. UINT8 Encoding; // Must be 1 for RLE. UINT8 BitsPerPixel; // Number of bits per pixel. UINT16 WindowMinX; // Left margin of image. UINT16 WindowMinY; // Upper margin of image. UINT16 WindowMaxX; // Right margin of image. UINT16 WindowMaxY; // Lower margin of image. UINT16 HRes; // Horizontal DPI resolution. UINT16 VRes; // Vertical DPI resolution. UINT8 ColorMap[48]; // 16-color palette. UINT8 Reserved; // Reserved, must be set to 0. UINT8 NumPlanes; // Number of planes. UINT16 BytesPerLine; // Bytes per scan line. UINT16 PaletteInfo; // Describes palette attributes. UINT16 HScreenSize; // Horizontal screen size, in pixels. UINT16 VScreenSize; // Vertical screen size, in pixels. UINT8 Filler[54]; // Header padding, set to 0. Here is a discussion of various fields: Manufacturer Unsigned 8-bit integer. This field contains the file manufacturer code number. It must be 10 for ZSoft PiCture eXchange. Version Unsigned 8-bit integer. This field is the code number of the version of PC Paintbrush that created the file. The field must be one of the following values: Value Meaning 0 PC Paintbrush 2.5 2 PC Paintbrush 2.8 (includes palette) 3 PC Paintbrush 2.8 (without palette) 4 PC Paintbrush for Windows 5 PC Paintbrush 3.0+ Encoding Unsigned 8-bit integer. This field specifies the encoding technique used to store image data. It must be 1 according to ZSoft specifications, although it is fairly common for programs to identify non-RLE files by setting this field to 0. Other values are not valid. Value Meaning 0 Raw encoding 1 Run length encoding BitsPerPixel Unsigned 8-bit integer. This field contains the number of bits per pixel per plane. It may be 1, 2, 4 or 8. See NumPlanes for more information. WindowMinX, WindowMinY, WindowMaxX, WindowMaxY Unsigned 16-bit integer. The inclusive boundaries of the drawing canvas, in pixels. HRes, VRes Unsigned 16-bit integer. These fields represent the horizontal and vertical resolution in which the image was created (either printer or scanner); i.e. an image which was scanned might have 300 and 300 in each of these fields. ColorMap[] An array of 48 unsigned 8-bit integers. This array contains color information used for a 16-color (or smaller) palette. See next section for more information on this topic. Reserved Unsigned 8-bit integer. This field is reserved and MUST be 0. NumPlanes Unsigned 8-bit integer. This field specifies the number of color planes. The maximum number of colors in the image can be determined using this field in conjunction with the BitsPerPixel field: NumPlanes BitsPerPixel Meaning 1 1 Monochrome using header palette. 1 2 4 colors using header palette. 1 4 16 colors using header palette. 1 8 256 colors using extended palette. 2 1 4 colors, see CGA Programming. 3 1 8 colors, RGB triples. 3 8 16 million colors, RGB triples. 4 1 16 colors, RGBI quads. 4 2 16 colors, RGBI quads. If NumPlanes is 1, the image is palette-indexed: each byte in the image data block contains (8 / BitsPerPixel) successive color indices and pixels are stored from low-order to high-order bits. Depending on the number of colors, the header palette or the extended palette is used. If NumPlanes is bigger than 1, no palette is used. Each byte in the image data block contains the color information of (8 / BitsPerPixel) successive pixels organized by color plane (RED GREEN BLUE INTENSITY): Scan line 0: RRR... GGG... BBB... III... Scan line 1: RRR... GGG... BBB... III... (etc.) BytesPerLine Unsigned 16-bit integer. This field contains the number of bytes per scan line per color plane, it must be an even value. PaletteInfo Unsigned 16-bit integer. This field specifies how the palette data must be interpreted; it must be one of the following values: Value Meaning 1 Color/Black and white 2 Grayscale HScreenSize, VScreenSize Unsigned 16-bit integer. Horizontal and vertical screen size, in pixels. These fields are only used in PC PaintBrush IV and PC PaintBrush IV Plus; other versions set these fields to 0. Filler[] An array of 54 unsigned 8-bit integers. Padding data, always set to 0. 3.0 COLOR MAP Decoding and interpreting colors is particularly tricky and few decoders get it right. By default, the palette is always located in the header and contains at most 16 colors. For files compatible with PC Paintbrush 3.x and more recent (version field is 5), a secondary 256-color palette may be included at the end of the file. If this palette is present, the header palette should be ignored. If NumPlanes is 1, the provided palette must be programmed into the graphic adapter. Technically, files with a higher NumPlanes field don't have color map information. However, if NumPlanes is 2, the CGA must be tweaked according to two bytes of information stored inside the header palette block. 3.1 CGA Programming Created for IBM-compatible PCs, early PCX format was dependent on IBM CGA hardware. While later adapters allowed each color attribute to be modified independently using RED GREEN BLUE triples, CGA boards did not allow as much flexibility. Instead, the 4-color palette had to be programmed globally by modifying bits on the CGA Mode Control Register (port 0x03D8) and Color Select Register (port 0x03D9). Two bytes stored in the ColorMap[] array (one at offset 0, the other at offset 3) are used to specify the CGA board tweaks. ColorMap[0] Background color (attribute 0) is determined in the upper four bits (value in range 0-15) and matches the default EGA color palette. Those four bits can be assigned to bit 3 to 0 of the CGA Color Select Register (INTENSITY RED GREEN BLUE respectively). ColorMap[3] Foreground palette (attributes 1 to 3) are determined by the 3 upper bits: Color Burst Enable, Palette and Intensity. These bits are interpreted as follows: Bits Meaning 0 Color burst enable: monochrome (set) or color (unset). Assign this value to bit 2 of the CGA Mode Control Register. 1 Palette: white (set) or yellow (unset). Assign this value to bit 5 of the CGA Color Select Register. 2 Intensity: bright (set) or dim (unset). Assign this value to bit 4 of the CGA Color Select Register. Ignoring the Color Burst Enable bit (which only makes sense on composite monitors), those bits provide four different palettes: Palette Colors IRGB IRGB IRGB Dim yellow dark green, red, brown 0010 =2 0100 =4 0110 =6 Dim white teal, purple, grey 0011 =3 0101 =5 0111 =7 Bright yellow green, peach, yellow 1010 =10 1100 =12 1110 =14 Bright white cyan, pink, white 1011 =11 1101 =13 1111 =15 3.2 Palette-indexed image, 16 colors PCX files version 2.5 and 2.8 (version field is 0 or 2) use the 48-byte long palette located in the header. Each of the 16 colors is stored as a 3-byte triple in RED GREEN BLUE order. Values may range from 0 to 255. 3.3 Palette-indexed image, 256 colors This palette may only exist in files compatible with PC Paintbrush 3.x or more recent (version field is 5). Read 769 bytes back from the end of the file. If the unsigned 8-bit integer located there is 12, the following 768 bytes are the 256-color extended palette. Each attribute is stored as a 3-byte triple in RED GREEN BLUE order. Values may range from 0 to 255. 4.0 ENCODED IMAGE DATA Pixels are stored left to right and top to bottom. Before decoding the file, obtain the width and height of the image: imageWidth = WindowMaxX - WindowMinX + 1 imageHeight = WindowMaxY - WindowMinY + 1 Since imageWidth may not necessarily match the number of bytes required to hold one complete uncompressed scan line (which may include unused data on the right side of the image), calculate the length of a scan line in bytes as follows: rowSize = numPlanes * BytesPerLine To decode, read one byte of data from the image data block; If the top two bits are set (RLEEnable), the remaining six bits (RLECount) specify how many times the next byte in the file must be written to the target buffer. If the top bits are not set, the byte is the data itself with an RLECount of one. Bits Meaning 5-0 RLECount; Number of times the following byte must be written to the target buffer. RLEPixels = 8 / BitsPerPixel * RLECount 7-6 RLEEnable; If both bits are set, read the following byte and copy it RLECount times to the target buffer. If only one of the two bits is set (or neither is), copy the byte to the target buffer once. The RLE compression works on a byte-level rather than a pixel-level. If BitsPerPixel is below 8, one byte contains multiple pixels. Repeating that byte multiple times will produce a pattern of (8 / BitsPerPixel) * RLECount pixels. Continue decoding the rest of the line and keep a running subtotal of how many bytes are moved and duplicated into the output buffer. When the subtotal equals rowSize (see BytesPerLine field), the scan line is complete. There will always be a decoding break at the end of each scan line, but there will not be a decoding break at the end of each plane within each scan line. Since there is always an even number of bytes per scan line, it is likely to find unused data at the end of each scan line; Use the imageWidth and imageHeight values to find where the valid image data is. If the data is multi-plane BytesPerLine shows where each plane ends within the scan line. There may also be extra scan lines at the bottom of the image, to round to 8 or 16 scan lines.