Write tiny images using PBM

0

The Portable Bit Map (“PBM”) image file format was invented in the 1980s as a way to transmit small black and white images as plain text. The PBM image format is one of a “family” of related images with basically the same image format, collectively called the “Portable aNy Map” (PNM) format.

PBM files have a very simple format that makes it easy for anyone to create a PBM file. This simplicity also made PBM briefly popular as an icon image format on monochrome systems.

Plain text PBM

The basic outline of any “PNM” image is a header section and a data section. The PBM header is the value P1 as the “magic number” (to indicate the file type) and two values for the width and height. All three entries must be separated by one whitespace character, which could include space, tab, or newline.

The PBM data section is a series of ones and zeroes that indicate black (1) or white (0). Because this is a single digit, any whitespace characters are safely ignored here.

For example, let’s say I wanted to draw an 8×8 black and white image of the “copyright” symbol: that’s just a “c” inside a circle. I might start by drawing the “dots” or “pixels” that make up the image on a piece of paper or in a scratch file. In this example, I might enter text into an editor, using periods for blank spaces and asterisks for black pixels:

..****..
.*....*.
*..**..*
*.*....*
*.*....*
*..**..*
.*....*.
..****..

After creating a sample image as plain text, it’s a simple process of search-and-replace in my editor to turn all periods to zeroes, and all asterisks to ones. Adding the magic number and the x and y dimensions makes this a valid PBM file:

P1
8 8
00111100
01000010
10011001
10100001
10100001
10011001
01000010
00111100

Web browsers don’t understand the PBM format, so here’s the PBM image converted to a more common PNG format, and “blown up” from 8×8 to 64×64 so you can see the pixels:

black and white image of a C inside a circle

Binary PBM

Storing a black and white image as a series of 1-byte characters isn’t very space-efficient: the values are either 0 (ASCII 48) or 1 (ASCII 49). Including the newline characters, this file is 79 bytes. A more compact way to write this is to use a raw binary pattern to represent each “off” (0) or “on” (1) value.

The basic format remains the same for this “raw” file: a header section and a data section. The header section is the same, except for a different magic number: plain text PBM files use P1 for the magic number, and raw PBM files use P4. This is because the Portable aNy Map “family” of file formats was initially just three files:

Magic NumberFile TypeColors
P1Portable Bit Map (PBM)Black and white
P2Portable Gray Map (PGM)Grayscale
P3Portable Pix Map (PPM)Full color

Each of the initial file types were plain text, so they could be sent over email. To make the file sizes smaller, and easier for programs to read and write the files, each file also has a raw binary file type:

Text FileRaw FileFile TypeColors
P1P4Portable Bit Map (PBM)Black and white
P2P5Portable Gray Map (PGM)Grayscale
P3P6Portable Pix Map (PPM)Full color

As another example, let’s say I wanted to draw an 8×8 empty box as a PBM image. I might start by drawing this on a piece of paper or in a text file, like this:

11111111
10000001
10000001
10000001
10000001
10000001
10000001
11111111

I’ve drawn the image as plain text in a text editor, using zeroes for white spaces and ones for black dots. To turn this into a raw PBM file, I need to break up the data into a series of bytes; this is convenient because I already defined the image as an 8×8 black and white image, and 8 bits is a byte.

In binary, bits are either 1 for “on” or 0 for “off,” and there are 8 bits in a byte. Each bit goes up in value by a power of 2, starting with 1 in the far-right position:

1286432168421Value
11111111255
10000001129

So to create a raw PBM file of an 8×8 empty box, I need to write the binary value 255, followed by six binary values of 129, and one more binary value 255. This is easiest to do in a program:

#include <stdio.h>

int main()
{
  puts("P4 8 8");
  printf("%c", 255);
  for (int i=0; i<6; i++) { printf("%c", 129); }
  printf("%c", 255);
  return 0;
}

This program writes a single binary 255 value, then six binary 129 values, followed by another binary 255 value. The program writes to standard output, so you need to redirect the output to a file when you run it.

$ gcc -Wall -o box box.c
$ ./box > box.pbm

The result is a raw PBM file that’s only 15 bytes long. Here’s a copy of the image, blown up to 64×64 size so you can see the pixels:

black and white image of an empty box

Portable images

PBM, PGM, and PPM files make it easy to create simple images on your own. Use Portable Bit Map (PBM) if you only need to create black and white images, like these sample images. To learn more about these images, see the Netpbm project hosted at SourceForge. The Netpbm Formats section in the user manual describes the file formats. See the pbm man page in the manual for a complete description of the PBM format.

Leave a Reply