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.:

(Where available, the canvas element is used here to show the image rendered from the data provided in the form.)

The image rendered from the data provided.
The PGM image, enlarged and encoded as PNG.
The PGM image (enlarged.)

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:

(Where available, the canvas element is used here to show the image rendered from the data provided in the form.)

The image rendered from the data provided.
The PBM image enlarged, tiled and encoded as PNG.
The PBM image, enlarged and tiled.

Or, alternatively: P1 4 4 1110101111010111.

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.:

(Where available, the canvas element is used here to show the image rendered from the data provided in the form.)

The image rendered from the data provided.
The PGM image enlarged and encoded as PNG.
The PGM image (enlarged.)

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):

(Where available, the canvas element is used here to show the image rendered from the data provided in the form.)

The image rendered from the data provided.

Portable Network Graphics

(Where available, the canvas element is used here to show the image rendered from the data provided in the form.)

The image rendered from the data provided.
The PNG image, enlarged.
The PNG image (enlarged.)

JPEG

(Where available, the canvas element is used here to show the image rendered from the data provided in the form.)

The image rendered from the data provided.
The JPEG image enlarged and encoded as PNG.
The JPEG image (enlarged.)