PCX Image Format
Unit: PCX
PCX (ZSoft Paintbrush) is a classic DOS image format (1985) widely used in retro games like DOOM, Duke Nukem, and Commander Keen. It is exported by Pix8, our own 8-bit pixel art editor, and by Aseprite — so modern pixel art workflows map onto it directly.
Overview
Format: ZSoft PCX v5 (256-color)
Color Mode: 8-bit indexed (256 colors, VGA Mode 13h compatible)
Compression: Simple RLE (Run-Length Encoding)
Palette: 768-byte RGB triplets at end of file
DOS Era: 1985-1995 (extremely period-correct)
Specifications
File Structure
[128-byte header]
[RLE-compressed pixel data]
[0x0C marker byte] ← Palette marker
[768-byte palette] ← 256 RGB triplets (0-255 range)
Header (128 bytes)
Offset |
Size |
Field |
Value |
|---|---|---|---|
0 |
1 |
Manufacturer |
10 (always) |
1 |
1 |
Version |
5 = 256-color |
2 |
1 |
Encoding |
1 = RLE |
3 |
1 |
BitsPerPixel |
8 |
4 |
2 |
XMin |
Left edge (usually 0) |
6 |
2 |
YMin |
Top edge (usually 0) |
8 |
2 |
XMax |
Right edge (Width-1) |
10 |
2 |
YMax |
Bottom edge (Height-1) |
12 |
2 |
HDpi |
Horizontal DPI |
14 |
2 |
VDpi |
Vertical DPI |
16 |
48 |
ColorMap |
EGA palette (unused) |
64 |
1 |
Reserved |
0 |
65 |
1 |
NPlanes |
1 (256-color) |
66 |
2 |
BytesPerLine |
Scanline width (may be padded) |
68 |
2 |
PaletteInfo |
1 = color |
70 |
2 |
HScreenSize |
Horizontal screen size |
72 |
2 |
VScreenSize |
Vertical screen size |
74 |
54 |
Filler |
Reserved (zeros) |
Important: Width = XMax - XMin + 1, Height = YMax - YMin + 1
RLE Compression
PCX uses extremely simple run-length encoding:
Byte Value |
Meaning |
|---|---|
|
Literal pixel value (no run) |
|
Run: (byte & 0x3F) = count, next byte = color |
Example:
0xC5 0x12 → 5 pixels of color 0x12
0x12 → 1 pixel of color 0x12 (literal)
0xCA 0xFF → 10 pixels of color 0xFF
Decoding Algorithm:
Read(byte);
if (byte and $C0) = $C0 then
count := byte and $3F;
Read(color);
Output color 'count' times
else
Output byte once (literal)
Palette
Located at EOF - 768 bytes
Preceded by marker byte 0x0C (at EOF-769)
Format: 256 RGB triplets, each 0-255
Conversion for VGA: Divide by 4 (0-255 → 0-63)
for i := 0 to 255 do
Read(R, G, B); { 0-255 range }
Palette[i].R := R shr 2; { Convert to VGA 0-63 }
Palette[i].G := G shr 2;
Palette[i].B := B shr 2;
Creating PCX Files
Pix8 (Recommended)
Pix8 is our own 256-color indexed pixel art editor — runs in the browser or as a Windows desktop app. Everything is 8-bit indexed by default, so there is no color mode to switch and no risk of an accidental truecolor export.
Create image (File → New…)
Size: 320×200 (full screen), 32×32 (sprites), 16×16 (tiles)
Already indexed (256 colors) — nothing else to set
Draw your pixel art
Use up to 256 colors
Color 0 = transparent (engine convention)
The GrafX2-style palette editor has a 6-bit VGA mode, matching the VGA DAC exactly
Export (File → Export as…, Ctrl+Shift+E)
Format: PCX
Save to
D:\ENGINE\DATA\YOURIMAGE.PCX
Pix8 also exports SPX (sprite XML + packed PCX sheets), so animated sprites can go straight from the editor into the Resource Manager.
GrafX2
The classic DOS-style pixel art editor. Roughly the same feature set as Pix8, and it still covers a couple of things Pix8 does not (dithering, gradient fill).
Draw with 256 colors
Save as PCX (RLE-compressed)
Aseprite
Create sprite (File → New)
Mode: Indexed (256 colors)
Size: 320×200 (full screen), 32×32 (sprites), 16×16 (tiles)
Draw your pixel art
Use up to 256 colors
Color 0 = transparent (engine convention)
Export (File → Export)
Format: .pcx
Color Mode: Indexed (8-bit)
✅ Ensure “Apply pixel ratio” is OFF
Save to
D:\ENGINE\DATA\YOURIMAGE.PCX
GIMP
Image → Mode → Indexed (256 colors)
File → Export As → .pcx
Options: “8-bit indexed color”
Photoshop
Image → Mode → Indexed Color (256 colors)
File → Save As → PCX
Format: “8 bits/pixel”
Using PCX.PAS
Interface
uses PCX, VGA;
function LoadPCX(const FileName: string; var Image: TImage): Boolean;
function LoadPCXWithPalette(const FileName: string; var Image: TImage;
var Palette: TPalette): Boolean;
function LoadPCXToFrameBuffer(const FileName: string; FrameBuffer: PFrameBuffer): Boolean;
function GetLoadPCXError: string;
Functions:
LoadPCX- Load image without palette (uses existing VGA palette)LoadPCXWithPalette- Load image with palette extractionLoadPCXToFrameBuffer- Load PCX directly to framebuffer (no intermediate TImage, optimized)GetLoadPCXError- Get last error message
Basic Example
uses VGA, PCX;
var
Img: TImage;
Pal: TPalette;
FB: PFrameBuffer;
begin
{ Load PCX with palette }
if not LoadPCXWithPalette('IMAGE.PCX', Img, Pal) then
begin
WriteLn('Error: ', GetLoadPCXError);
Halt(1);
end;
{ Display }
InitVGA;
SetPalette(Pal);
FB := CreateFrameBuffer;
PutImage(Img, 0, 0, False, FB);
RenderFrameBuffer(FB);
ReadLn;
{ Cleanup }
FreeFrameBuffer(FB);
FreeImage(Img);
DoneVGA;
end.
Load Without Palette
{ Use when palette already set (e.g., from another image) }
if LoadPCX('SPRITE.PCX', Img) then
PutImage(Img, 100, 50, True, FrameBuffer);
Load Directly to FrameBuffer (Optimized)
uses VGA, PCX;
var
FB: PFrameBuffer;
Pal: TPalette;
begin
InitVGA;
FB := CreateFrameBuffer;
{ Load PCX directly to framebuffer - no intermediate TImage needed }
if not LoadPCXToFrameBuffer('BACKGROUND.PCX', FB) then
begin
WriteLn('Error: ', GetLoadPCXError);
Halt(1);
end;
{ Display }
RenderFrameBuffer(FB);
ReadLn;
{ Cleanup }
FreeFrameBuffer(FB);
DoneVGA;
end.
Benefits:
No intermediate
TImageallocationNo
PutImagecall neededSaves memory and CPU time
Perfect for loading backgrounds directly to BackgroundBuffer
Testing
# Compile PCX loader and test
cd TESTS
CPCXTEST.BAT
PCXTEST.EXE
Test program loads DATA\TEST.PCX and displays it with palette.
Size Limits
Max image size: 65,520 bytes (DOS GetMem limit)
Max dimensions: 65,535×65,535 (Word type)
Practical limits:
Full screen: 320×200 = 64,000 bytes ✅
Large sprite: 320×204 = 65,280 bytes ✅
Oversized: 320×205 = 65,600 bytes ❌
Technical Notes
Scanline Padding
PCX may pad scanlines to even byte boundaries:
BytesPerLine ≥ Width
Extra bytes at end of each scanline are discarded
PCX.PAS handles this automatically
Palette Marker
Some PCX files omit the 256-color palette (rare). PCX.PAS:
Checks for marker byte 0x0C at EOF-769
If missing: uses grayscale fallback palette
Continues loading (non-fatal warning)
VGA Color Conversion
PCX palette uses 0-255 range, VGA DAC uses 0-63:
VGA_R := PCX_R shr 2; { Divide by 4 }
VGA_G := PCX_G shr 2;
VGA_B := PCX_B shr 2;
Turbo Pascal Compatibility
Uses BlockRead (untyped files)
No external dependencies (except VGA.PAS)
Real mode safe (no extended memory)
16-bit pointer arithmetic
References
ZSoft PCX Specification: Available at FileFormat.Info
Pix8 (recommended, PCX + SPX export): https://pix8.app
GrafX2 (for PCX): http://grafx2.chez.com/
Aseprite: https://www.aseprite.org/
See Also
VGA.md - Graphics API reference
Palette Workflow - Splitting the palette between UI and artwork
Graphics & Input - All graphics and input modules