Palm Native Image Format

This page contains a short summary of the Palm native image format. Thanks to Ross Thompson for the corrections, the gif conversion C-programm, and the description of the compression formats!

2 byte quantities are packed in "big endian" order (most significant byte first.

Offset (bytes) Size (bytes) Name Description
0 2 width Width of the image (pixels)
2 2 height Height of the image (pixels)
4 2 rowBytes Number of bytes in each row of the image (must be even)
6 2 flags 0x8000: Compressed
0x4000: hasColorTable
0x2000: Transparency
8 1 pixel size The size of a pixel in bits (1, 2, 4 or 8); n bits allow 2n colors
9 1 version The version number of the data structure.
0: Palm OS 1
1: Palm OS 3, no transparency, no RLE compression.
2: Palm OS 3.5; transparency and RLE compression supported
10 2 nextDepthOffset Palm images may consist of a set of structures for different screen color depths. If so, this field contains the number of four byte words to the start of the next image from the start of this structure
12 1 transparent index Index of the transparent color if the transparent flag is set. Not used in versions 0 or 1
13 1 compression type Type of compression, not supported for versions 0 or 1:
0: ScanLine
1: RLE
14 2 reserved Reserved for future extensions
16 variable data Color table and Image data

If the image has a color table, the next two bytes determine the number of four byte color table entries of the following structure:

  1. reserved (should be 0)
  2. Red value
  3. Green value
  4. Blue value

The actual pixel data are represented as a byte vector, where 1 to 8 pixel values are stored in one byte, depending on the bit size of a pixel. If multiple pixels are in a single byte, the most significant bits correspond to the left-most pixel. Pixel values are indexes into a color table. If no color table is provided with the image, the system color table is used. For uncompressed images, the scanlines are exactly rowBytes long. For compressed images, a two byte integer precedes the pixel data, which indicates the length of the image data (pixel data + 2 bytes for the count.)

Notes on compression

Scanline compression

Scanline compression takes advantage of similarity on the vertical axis. Each raster (after the first) only needs to provide data bytes that are different from the previous scan line. This is best described with an example. Let us assume we are trying to draw diagonal lines two pixels wide. (For simplicity, we will use 8 bit pixels in the example, though such an image really only requires 1 bit pixels.)

The uncompressed data for this (13x13) image might look like this (169 bytes):

25 25 88 88 25 25 88 88 25 25 88 88 25
25 88 88 25 25 88 88 25 25 88 88 25 25
88 88 25 25 88 88 25 25 88 88 25 25 88
88 25 25 88 88 25 25 88 88 25 25 88 88
25 25 88 88 25 25 88 88 25 25 88 88 25
25 88 88 25 25 88 88 25 25 88 88 25 25
88 88 25 25 88 88 25 25 88 88 25 25 88
88 25 25 88 88 25 25 88 88 25 25 88 88
25 25 88 88 25 25 88 88 25 25 88 88 25
25 88 88 25 25 88 88 25 25 88 88 25 25
88 88 25 25 88 88 25 25 88 88 25 25 88
88 25 25 88 88 25 25 88 88 25 25 88 88
25 25 88 88 25 25 88 88 25 25 88 88 25

Starting for the moment with the second raster, we see that every other byte is the same as the corresponding byte from the previous raster. We encode the first 8 bytes of the second raster by first writing a flag byte that indicates we will be supplying only the second, fourth, sixth, and eighth bytes. This byte is binary 01010101, or 0x55. We then supply only the bytes that correspond to bits set for the flag word. Generalizing this to the entire second raster, we obtain (with flag bytes in bold)

55 88 25 88 25 50 88 25

The only gimick is that the first row has no prior row, so it must be specified completely. So, the representation of the first row would be

FF 25 25 88 88 25 25 88 88 F8 25 25 88 88 25

So, the image data (including the two byte length at the top) would be represented (in 119 bytes) thus:

00 77
FF 25 25 88 88 25 25 88 88     F8 25 25 88 88 25
55 88 25 88 25                 50 88 25
AA 88 25 88 25                 A8 88 25 88
55 25 88 25 88                 50 25 88
AA 25 88 25 88                 A8 25 88 25
55 88 25 88 25                 50 88 25
AA 88 25 88 25                 A8 88 25 88
55 25 88 25 88                 50 25 88
AA 25 88 25 88                 A8 25 88 25
55 88 25 88 25                 50 88 25
AA 88 25 88 25                 A8 88 25 88
55 25 88 25 88                 50 25 88
AA 25 88 25 88                 A8 25 88 25

(Note: The formatting and spacing is for readability only.)

Run Length compression

Run length compression takes advantage of horizontal repetition rather than vertical repetition. Runs are sequences of bytes all the same, and are encoded with two bytes, the first being the length, and the second as the value to be repeated. The data are represented as a series of runs, except that each scan line is encoded as a separate unit. (Runs cannot span raster boundaries.) Runs that are longer than 255 bytes are split, since the length byte can not have a value larger than 255. This is not a real restriction, in practice, since the Palm only has 160 pixels of display width.

Encoding the above example with run length encoding would yeild the following (184 bytes, including the two byte length):

00 B8
02 25 02 88 02 25 02 88 02 25 02 88 01 25
01 25 02 88 02 25 02 88 02 25 02 88 02 25
02 88 02 25 02 88 02 25 02 88 02 25 01 88
01 88 02 25 02 88 02 25 02 88 02 25 02 88
02 25 02 88 02 25 02 88 02 25 02 88 01 25
01 25 02 88 02 25 02 88 02 25 02 88 02 25
02 88 02 25 02 88 02 25 02 88 02 25 01 88
01 88 02 25 02 88 02 25 02 88 02 25 02 88
02 25 02 88 02 25 02 88 02 25 02 88 01 25
01 25 02 88 02 25 02 88 02 25 02 88 02 25
02 88 02 25 02 88 02 25 02 88 02 25 01 88
01 88 02 25 02 88 02 25 02 88 02 25 02 88
02 25 02 88 02 25 02 88 02 25 02 88 01 25

(That is, for the first row, there would be "two" bytes of "0x25", "two" bytes of "0x88", and so forth.)