Notes on image formats
We consider several image formats, starting with those simpler to examine with generic tools (text and hex editors) and proceeding to those of practical importance, such as JPEG and PNG. We employ here a set of JavaScript libraries and functions to allow the reader, where JavaScript is available, to experiment with said formats by entering arbitrary image data into provided forms and seeing it rendered when the form is submitted to the embedded code. For the examples listed, we also provide equivalent images enlarged and encoded as PNG for compatibility with Web user agents. This is a work in progress.
Portable Anymap
Portable Anymap is a family of raster graphics interchange formats introduced in the 1980s, which since includes seven distinct variants: two Portable Bitmap (.pbm
) formats, two Portable Graymap (.pgm
), two Portable Pixmap (.ppm
), and a single Portable Arbitrary Map (.pam
) one.
The simpler to demonstrate here are ASCII-based .pgm
and .pbm
formats. Consider, e. g.:
Here, the P2 5 7 3
header describes the data as ASCII-based grayscale image (.pgm
), 5 pixels wide and 7 pixels high, with four shades of gray, 0 through 3, where 0 denotes black and 3 white.
The 35 values that follow give the shades of every pixel starting with the top left and proceeding in row-by-row order. The whitespace characters that separate the values may include blanks (ASCII code 32), horizontal tabulation (9), line feeds (10) and carriage returns (13.) For clarity, each row of values in the example above is terminated with a line break, but this is not compulsory: any non-empty sequence of whitespace characters is allowed by the format itself.
In the Portable Bitmap case, indicated by the leading P1
string, every pixel is encoded by a single bit of data, which makes encoding both the number of values (or, rather, the maximum value, or maxval) in the header and the use of whitespace between the values themselves unnecessary. As such, maxval is not permitted, while whitespace in the data is entirely optional. An example image may be encoded as follows:
Or, alternatively: P1 4 4 1110
.
Conversely, in the Portable Pixmap case, indicated by the leading P3
string, every pixel is encoded by three adjacent values, giving the intensities of red, blue and green (RGB) color components. Aside of there being thrice as many values, the format is essentialy the same as .pgm
described above.
Let us note that encoding 35 values in the .pgm
example above takes 70 bytes of data, even though there’re only 3 × 35 = 105 bits, or less than 13 octets, to encode.
This, fortunately, can be improved by encoding values directly as bytes, forgoing writing them as ASCII decimal numbers. The use of this convention is indicated by the use of P4
, P5
and P6
in place of P1
, P2
and P3
in the header, respectively. Consider, e. g.:
In this example, the image is 4 by 4 pixels, and uses 16 shades of gray, each encoded as a single byte, following a header of 12 bytes (of which 2 are unnecessary whitespace and are only used for values to line up in the hexadecimal dump above.) It’s still inefficient: a full octet is used where only half of one would suffice, but it’s better than the use of two (for values 0‒9) or three (10‒15) for ASCII decimals. This format shows its peak efficiency when encoding images having maximum values of 255 or 65535.
Even more efficient is the binary .pbm
format, where each octet encodes up to 8 pixels. Hence, the .pbm
image above can be encoded as follows (once again adding an otherwise unnecessary blank for the values to line up):