The Poly-Raster Image format
At a glance
| Extension | *.pri |
| Current version |
0.3 (Software & tools; updated on 2007-01-06) 0.2 (Specification of the file format; updated on 2007-10-24). |
| Downloads |
Image converters (source code and binaries). Image loader implementations (commented source code). |
| Documentation |
This page contains the full documentation of the format, plus a
rationale for the design.
The technical description of the file format
is further down on this page. Descriptions of the lay-outs of various displays and controllers are listed in the technical description too. |
Introduction
The Poly-Raster Image format (file extension: ".pri") is a picture file format that holds one or more device-specific bitmaps along with descriptive headers. The Poly-Raster Image format is "yet another" image file format and the first (rhetoric) question to answer, then, is why we need to add another format to the many that exist already?
The goal of the Poly-Raster format is to be a flexible format that is easy to decode, using few resources. In particular, we should not assume that the loader/decoder can keep the entire image in memory. The Poly-Raster format is therefore particularly useful for embedded systems --especially the smallest of these embedded systems.
The display hardware in the "desktop PC" category is quite universal: memory is arranged in "scan lines" with the lowest address in video memory indicating the upper left pixel. Displays for embedded systems are more diverse: for example, for LED sign displays and wide graphic LCDs & VFDs, the display memory is often arranged in columns rather than in rows. Picture files for such displays are often in proprietary formats and only usable on a narrow range of devices. The Poly-Raster Image format brings three advantages over proprietary bitmap formats:
- It saves work. You do not need to write image converters, because these are already available. Image loaders can be ported from existing implementations or adapted from well-commented reference implementations.
- It is easier to support a wider range of displays, since a poly-raster file may hold several bitmaps with different display definitions, resolutions or colour depths.
- It is a documented format, rather than the source code dump that proprietary formats often supply as "documentation" --if any at all. By being described and maintained, the format has a higher chance of survival.
The reasoning behind the Poly-Raster format is that the image loader (on the embedded device) needs to support only a single sub-format. The image encoder, running on a desktop PC, has ample resources to perform the conversion from source image data to Poly-Raster images. In this case, the encoder has to convert the image data to a specific display --if the format does not match the one that the embedded device accepts, the file will simply be rejected. If you wish to make an image that is accepted by several devices, you can ask the encoder to build a multi-bitmap file. Each embedded image loader now chooses the entry that has the format that it supports.
In comparison with flexible and extensible image formats like TIFF, the Poly-Raster format avoids the complexity of parsing various options in order to interpret the pixel data, and the requirement to buffer the image data in RAM if the output device uses a column-ordered or banded lay-out. Instead, the pixel data is present multiple times in a poly-raster file and the loader chooses the most appropriate format. In comparison with "light" formats like WBMP, the Poly-Raster Image format is well-defined. Apart from the description of "sub-type 0" ("scan line" order, monochrome, maximum size 255 × 255 pixels) the WBMP specification essentially tells you that you are completely on your own to define formats for larger images or for a different lay-out of display memory. The WBMP specification, hence, is of no help at all.
File format
A ".pri" file is a collection of bitmaps. There may be only one bitmap in a file, or there may be several. Each bitmap has a header, but there is no "file header" describing the collection of bitmaps. It is up to the implementer or producer of the image to decide which bitmap formats are present in a file (and in what order).
The header of each bitmap gives the details of the format and the pixel lay-out. All multi-byte fields in the header are in Little Endian ("Intel" format). The header itself is 14 bytes in length.
| field | size | description |
|---|---|---|
size | 4 | the number of bytes that the bitmap takes in the file, including the header; if zero, it indicates a file terminator |
id | 2 | a signature, which must be the value A201 (hexadecimal) |
layout | 2 | bitmap type and flags, see the section "Bitmap lay-outs" for details |
width | 2 | the width of the bitmap in pixels |
height | 2 | the height of the bitmap in pixels |
depth | 1 | the "colour depth", meaning the number of bits per pixel |
chksum | 1 | this field is a checksum over the entire bitmap data including the header |
An image file may contain multiple bitmaps and each bitmap will have a header. This way, a loader may pick the most appropriate bitmap from the file. In fact, most loaders will simply take the first bitmap in the file that they can support, so it is advised to store the highest resolution, most colourful images first in the file.
When browsing through the image file, the loader jumps from one bitmap to the
next using the size field in the header. To see whether the end is reached, a
loader can use the file size. In absence of a file system, e.g. streaming images,
an alternative is to append a terminator to the image --the terminator is a four-byte
field with all four bytes set to 0. When a loader tries to read a new header, the
terminator takes the place of the size field, so a bitmap size of
zero means that no more data will follow.
The bitmap data follows the header (plus an optional extended header) directly,
without any padding. However, if there is a colour map ("palette") in the bitmap,
the colour map precedes the pixel data. The layout field has bits
to indicate the presence of an extended header and/or a colour map. The order
of the data for a single bitmap is thus: header, (extended header),
(colour map), pixel data; where the items between parentheses
are only present if the respective bits in layout field are set.
The extended header is discussed separately in the section on animation.
If present, the colour map holds a number of three-byte entries with RGB components, in the order red-green-blue. The number of entries in the colour map depends on the colour depth: it is 2 for 1-bpp bitmaps, 4 for 2-bpp bitmaps, 16 for 4-bpp bitmaps and 256 for 8-bpp bitmaps; only pictures with 8-bpp or less can have a colour map. When no colour map is present in a bitmap and the colour depth is 8 or less, the bitmap uses a device-specific default palette.
An image loader can verify the checksum by summing up all bytes in the bitmap, including the bytes in the header, the extended header and the colour map. The resulting value should be 255 --a byte with all bits set. While summing the bytes, the image loader should use the bytes as stored in the poly-raster file, without unpacking pixels or applying any transformations. The summing is in two's complement and with truncating to 8-bits, meaning that 255 + 1 sums to 0. The image encoder calculates the checksum by first storing zero in the checksum field, computing the sum over all data and then subtracting the result from 255.
Bitmap lay-out
The layout field defines the lay-out of the pixels in the bitmap.
| bit | description |
|---|---|
| 0 | Column order: if set the pixels are ordered in columns rather than in rows ("scan lines") |
| 1 | Banded lay-out: if set, each byte represents a set of pixels in the opposite lay-out as the general lay-out |
| 2 | Reversed pixel order: if set, bit 0 of the first byte is part of the top left pixel; otherwise bit 7 of the first byte is part of the top left pixel. For RGB images, this flag indicates that the colour order in each pixel is BGR instead of RGB. |
| 3 | Planar: if set, the depth field refers to the bit depth of planes rather than the number of bits-per-pixels
|
| 4 | Inverted Y-axis: if set, coordinate (0,0) is the lower left corner of the display rather than the upper left corner |
| 5 | Extended header present: if set, an extended header is present (immediately after the header) |
| 6 | Colour map present: if set, a colour map is present (it comes after the extended header, or after the header in absence of an extended header) |
| 7 | Loop frame: if set, this image is a loop-back frame at the end of an animated sequence |
The next few figures give some examples for the bitmap lay-outs. In each case,
the bitmap is a monochrome (1-bpp) picture. The first figure has all bits in the
layout field set to zero. This is a common bitmap lay-out for standard
PC graphics and image file formats; for example, the monochrome BMP and PBM image
file formats use this very lay-out. In the figure, memory address zero is in the
upper left. Vertically, the address jumps with "row" bytes and horizontally, it
increments with 1 byte.
Column order is common for displays and LED signs where data has to scroll horizontally (rather than vertically). You might almost say that a display with column order looks similar to a row-order display that is rotated 90°. In the figure, memory address zero is in the upper left. Horizontally, the address jumps with "col" bytes and vertically, it increments with 1 byte.
A banded lay-out mixes row and column modes. From a memory addressing perspective, a banded row-order lay-out is indeed row-order: the address increments by one along the horizontal axis. However, a row is now eight pixels high, rather than just one pixel. A banded row-order lay-out is common for dot matrix printers, ink-jet printers and thermal transfer printers.
Reversed pixel order only changes the order of the pixels in a byte, in the case
that multiple pixels fit in a byte. Compare the figure below to that of the
layout = 0x00 case.
The bits in the lay-outs may be combined too. I have only presented a lay-out for a banded row-order bitmap, but in similar ways, you can imagine a banded column-order bitmap.
Depending on the number of bits per pixel, some of the lay-out bits are irrelevant.
For example, for an 8-bpp image there is exactly one pixel in a byte and bit 2
in the layout field does not make sense (except for the very special
case that an 8-bpp device with be planar, but I am unaware of such devices).
Similarly, 1-bpp images are never "planar", as there is only one plane. Only
1-bpp images can be "banded". Image writers should clear any irrelevant flags.
The table below lists the lay-outs for a few well-known devices. The "label" column refers to the symbolic name that you can use in the command-line image converter utilities.
| controller | lay-out | label | description |
|---|---|---|---|
| VGA, PC-graphics | 0x00 | vgamono | The most common lay-out for standard PC video cards and bitmap file formats (GIF, PNG, PCX, binary PBM, etc.). The data is ordered in "scan lines" with the most significant bit of a byte denoting the left-most pixel (for monochrome images). |
| Windows BMP | 0x10 (inverted Y-axis) | bmp | The Microsoft Windows BMP file format uses (0,0) as the left-bottom coordinate. |
| Dot matrix printer | 0x02 (banded rows) | esc_p2 | Most dot-matrix and thermal printers use the ESC/P2 control language originally developed by Epson. The command set is primarily aimed at ASCII text, but it includes commands for single-precision and double-precision graphics. The graphics data must then be sent as banded rows. |
| KS0107/KS0108 | 0x06 (banded rows, reversed pixel order) | ks0108 | The controller pair KS0107/KS0108 drives a 64×64 pixel area. In a typical set-up, two KS0108 controllers are combined with a single KS0107 controller to form a 128×64 resolution display. Compatible alternatives to the KS0107/KS0108 are: HD61202/HD61203, AX6108/AX6107, KS0708, S6B0708, S6B0108A, S6B2108, S6B0108 and NT7108. |
|
Noritake 372 Noritake 900 Noritake 3000 | 0x01 (column order) | gu372 gu900 gu3000 | The Noritake-Itron 372 series, Noritake-Itron 900 series and Noritake-Itron 3000 series, all use column order for the graphic data. Displays from these series are, for example, the GU256x64-372, the GU256x64-900a and the GU256x64-3900. |
| Noritake 7000 | 0x06( banded rows, reversed pixel order) | gu7000 | The Noritake-Itron 7000 series uses a lay-out similar to that of the KS0107/KS0108 controllers. |
| Noritake 7800 | 0x00 to 0x03 (multiple configuration) | gu7000 | The Noritake-Itron 7800 series has a configurable lay-out for graphic data; it can either use a VGA-compatible "scan line" lay-out (0x00), column-mode, banded mode, and banded column mode. Popular displays from this series are the GU140x32F-7806 and the GU140x16G-7806. |
Pixel encoding
If a bitmap has planes (and assuming a row-encoded bitmap), the file holds in sequence the pixels for layer 0 for a row, then layer 1 for that row, and so forth for the number of planes, before moving on to row 1 (and restarting with layer 0). Column-encoding bitmaps follow the same procedure, but with columns instead of rows.
A row or column (or plane in a row or column) is always padded up to a full byte; no partial bytes are stored. This is only relevant when the number of bits per pixel is less than 8. Other than writing full bytes, no other padding occurs.
Currently, no compression scheme is defined, as we have not found a simple compression algorithm that performs well on image data and that does not require RAM tables. In current configurations, the images are usually small and monochrome (1 bit-per-pixel).
Animation with poly-raster images
The poly-raster format allows for simple animations by storing several bitmaps
in a file that all have the same value for the layout field. The
first bitmap is a full and plain image; all other bitmaps have an extended header
that gives the amount of time to wait before putting the next frame on screen,
plus deltas for the horizontal and vertical position of the section of the image
that changes from the previous frame.
The presence of an extended header must be signalled with bit 6 in the layout
field in the standard header. If present, the extended header follows the standard
header immediately. The fields in the extended header are in the following table.
| field | size | description |
|---|---|---|
delay | 2 | the number of milliseconds to wait before displaying this bitmap, relative to the time that the previous bitmap was displayed |
dx | 2 | a value to add to the horizontal position where the pixel is displayed |
dy | 2 | a value to add to the vertical position where the pixel is displayed |
The dx and dy fields allow, in combination with the
width and height fields of the header, to restrict the
stored bitmap to a rectangular area inside the full image. If only part of the
frame changes, only that part needs to be stored in the bitmap of the frame.
The (dx, dy) and (width, height) coordinates are restricted to indicate a byte-aligned
subset of the image. For example, if the image is monochrome (1 bit-per-pixel) and in
row-order, the dx and width are aligned to multiples
of eight pixels. For a column-ordered monochrome image, the dy and
height fields are aligned to eight pixels. For a 2 bits-per-pixel image,
the alignment would be a multiple of four pixels, since four pixels fit a byte in this
case. These alignment restrictions make it easier on the image loader, because no
partial bytes need to be processed.
The delay field indicates the amount of time (in milliseconds) that
should pass before the frame appears on the screen. In other words, it
indicates how long the previous frame should be displayed.
One can, of course, combine animations with multiple lay-outs in a file. The
bitmaps that have the same layout code are part of an animation for
that lay-out. Bitmaps with different layout codes in the same file
are part of animations for a different display.
Image readers that do not support animations read the first (full) image only.
To support animation, an image reader should look for bitmaps with the same
layout code and an extended header further on in the file.
When an animation is a "looping" animation, the last frame should have bit 7 in the
layout field set. This field indicates that the respective frame brings
back the image to be identical to the first (full) image. After the image reader
loads this image, it jumps back to the second frame in the animation.
That is, an image reader decodes the full image, the one without an extended
header, only once, as the very first image.
Miscellaneous notes
When the images are transferred in real-time rather than present in (ROM) memory or on a disk, instead of sending each device every supported format, a server could negotiate which format the client accepts, and send poly-raster files with only the bitmap in that format.
If the image generator were to create an image with bitmaps in every possible lay-out, it would need to include 16 bitmaps for a monochrome image, and more for colour images, especially when the colour image also includes fall-back bitmaps for monochrome displays. In practice, an application or server creates images for a particular group of devices. In this group, some uniformity may be expected. For example, for an application for creating scrolling banners for LED signs may restrict itself to column ordered formats, which are easier to scroll horizontally.
Although a loader may opt to support exactly one bitmap format, it may also choose to do some conversions itself. For example, when a display is laid out in rows (scan lines), it takes only little extra code to supporting an inverted Y-axis in addition to a normal Y-axis. To support both bit orders, you could add a table with reverse byte mapping, which takes 256 bytes of ROM and no RAM.
An image writer should always store a checksum, but it is entirely up to the image loader whether it verifies checksums in images. Verifying the checksum makes the loader a little more robust, but it also increases the complexity of the loader. Many network protocols and storage media are quite reliable, and the extra bit of robustness may not be worth the complexity that it adds to the code.
Software downloads
Image converters
-
Portable version: converts images in Portable Bit-Map and
Portable Gray-Map formats (PBM and PGM) format to poly-raster images. This is
a command line utility that comes as source code.
Download
(11 kiB; 2007-01-06)
The current version of the utility only supports monochrome and 8-bpp (grey level) poly-raster images. -
Microsoft Windows: utility that converts images in BMP
(Microsoft Windows Bitmap) to ".pri" files. It also supports the PBM and PGM
formats (like the portable version). There are both a command utility
and a GUI utility in the ZIP file.
Download
(66 kiB; 2007-01-06)
The current version of the utility only supports monochrome and 8-bpp (grey level) poly-raster images.
Screen shot of the GUI version of the poly-raster image converter
Image loaders
All image loaders come with source code. The portable versions create a PBM image from the poly-raster file, extracting the best matching bitmap. The Microsoft Windows versions display the bitmap after loading it.
-
Memory block (portable)
Loads the matching bitmap entirely in memory before decoding it and writes its output to a file in the PGM format. This is the simplest implementation, but it assumes that you have enough memory to hold both the entire bitmap, and that you can perform random access in the file.
Download
(2.2 kiB; 2006-12-29)
-
Byte I/O (portable)
Loads the matching bitmap byte-for-byte, and writes the destination bitmap byte-for-byte too. This takes very little memory, but it is fairly slow. This example program goes through the file one byte at a time, without jumping forward or backward. As such, it is a good basis for implementing loaders that must support streaming images.
Download
(2.2 kiB; 2006-12-29)
-
Buffer I/O (portable)
Loads the matching bitmap in (small) buffers. This is a compromise between memory and speed; it is also the most complex implementation, because it assumes that the buffer size is fixed and unrelated to the size of a scan line or column.
Not yet available -
Memory block (Microsoft Windows)
Loads the matching bitmap entirely in memory before decoding it and then displays the image output in a window. This example image loader is a variation of the portable "memory block" implementation. It supports animations in addition to single-frame poly-raster images.
Download
(3.9 kiB; 2006-12-29)
