The PCX format is a relatively simple format that provides a minimum of compression using Run Length Encoding (RLE). RLE means that the file can be read from start to finish in one pass and encoded or decoded without any holistic information (i.e., in order to figure out what the next encoded byte is, you only have to know what preceded it, not anything after it.) The PCX format is especially useful for 320x200x256 VGA mode 13h (where each pixel is stored as a byte). The PCX format was originally used by PC Paintbrush.
The following discussion assumes 320x200x256 VGA mode 13h, as described in Section 11.2
Two types of bytes are stored in the data image portion of a PCX file. One type is a length, and the other is color. A length byte is specified by the two upper bits being set. This limits the length specified by a length byte to 64. The other type is a color byte, and specifies a value for the byte from the palette table (the palette holds the actual RGB values of the color, and the color byte is an index into this table). This is the same method used in mode 13h. The first byte from the data is read. If the two upper bits are set, then it is a length byte, and the next byte is the color which will be replicated as many times as stated by the length byte, from left to right on the screen, ending at the end of a line (see BYTES_PER_LINE below). If the two bits are not set, then it is a color byte, and it goes onto the screen in the next location (left to right) as is.
Note: Any color greater than or equal to 192 cannot be stored as a single color byte, and must be a given a length first. For instance, if you have a single byte of color 192, then it must be represented by two bytes of 193 (length byte of 1) and 192 (color byte 192).
The PCX file itself contains two parts--the first part is called the header, which contains information about the image; the second part is the image data, which contains actual image data and color information. Rather than explain each field of the header in detail, a structure is shown below which gives a brief glance at the purpose of each field.
STRUC PCX_Header .Manufacturer resb 1 ; should always be 0Ah .Version resb 1 ; .Encoding resb 1 ; should always be 01h .BitsPerPixel resb 1 ; .XMin resw 1 ; image width = XMax-XMin .YMin resw 1 ; image height = YMax-YMin .XMax resw 1 .YMax resw 1 .VertDPI resw 1 ; .Palette resb 48 ; .Reserved resb 1 .ColorPlanes resb 1 ; .BytesPerLine resw 1 ; .PaletteType resw 1 .HScrSize resw 1 ; only supported by .VScrSize resw 1 ; PC Paintbrush IV or higher .Filler resb 56 ENDSTRUC
0 -- Version 2.5 |
2 -- Version 2.8, palette included |
3 -- Version 2.8, use default palette |
5 -- Version 3.0 or better |
1 -- Monochrome |
4 -- 16 colors |
8 -- 256 colors |
24 -- 16.7 million colors |
4 -- 16 colors |
3 -- 24 bit color (16.7 million colors) |
In a PCX file containing 16 colors of less, the palette is contained in the .Palette section of the header. In a PCX file containing 256 colors, the palette is at the end of the file, and takes up the last 768 bytes (256 * 3 bytes per color RGB). If the last 768 bytes is a palette, there is a padding byte preceding it in the file (whose value is 12).
Example 11-1. Displaying a PCX File
EXTERN kbdin, dosxit ; LIB291 functions SEGMENT ScratchSeg ScratchPad resb 65535 SEGMENT stkseg STACK resb 64*8 stacktop: resb 0 SEGMENT code PCX1 db 'my_pcx1.pcx', 0 ; Filenames PCX2 db 'my_pcx2.pcx', 0 ; (Must end with 0 byte) ..start: mov ax, cs ; Set up data and stack segments mov ds, ax mov ax, stkseg mov ss, ax mov sp, stacktop MAIN: ; Sets up mode 13h and clears screen mov ax, 0013h int 10h mov dx, pcx1 ; Filename to display call ShowPCX ; Display PCX file to screen ; Wait for keypress call kbdin ; Go back to text mode mov ax, 0003h int 10h ; Return to DOS call dosxit ;----------------------------------------------------------------------------- ; ShowPCX procedure by Brandon Long, ; modified by Eric Meidel and Nathan Jachimiec, ; converted to NASM, cleaned up, and better commented by Peter Johnson ; Inputs: DX has the offset of PCX filename to show. ; Output: PCX file displayed (all registers unchanged) ; Notes: Assumes PCX file is 320x200x256. ; Uses ScratchSeg for temporary storage. ; The PCX file must be in the same directory as this executable. ;----------------------------------------------------------------------------- ShowPCX push ax ; Save registers push bx push cx push si push di push es mov ax, 3D00h int 21h ; Open file jc .error ; Exit if open failed mov bx, ax ; File handle mov cx, 65535 ; Number of bytes to read mov ax, ScratchSeg ; DS:DX -> buffer for data mov ds, ax mov dx, ScratchPad mov si, dx mov ah, 3Fh int 21h ; Read from file mov ax, 0A000h ; Start writing to upper-left corner mov es, ax ; of graphics display xor di, di add si, 128 ; Skip header information xor ch, ch ; Clear high part of CX for string copies .nextbyte: mov cl, [si] ; Get next byte cmp cl, 0C0h ; Is it a length byte? jb .normal ; No, just copy it and cl, 3Fh ; Strip upper two bits from length byte inc si ; Advance to next byte - color byte lodsb ; Get color byte into AL from [SI] rep stosb ; Store to [ES:DI] and inc DI, CX times jmp short .tst .normal: movsb ; Copy color value from [SI] to [ES:DI] .tst: cmp di, 320*200 ; End of file? (written 320x200 bytes) jb .nextbyte mov cl, [si] cmp cl, 0Ch ; Palette available? jne .close ; Set palette using port I/O mov dx, 3C8h mov al, 0 out dx, al inc dx ; Port 3C9h mov cx, 256*3 ; Copy 256 entries, 3 bytes (RGB) apiece inc si ; Skip past padding byte .palette: lodsb shr al, 1 ; PCX stores color values as 0-255 shr al, 1 ; but VGA DAC is only 0-63 out dx, al dec cx jnz .palette .close: mov ah, 3Eh int 21h ; Close file .error: pop es ; Restore registers pop di pop si pop cx pop bx pop ax ret