Picture format

From DoomWiki.org

Many of the Doom engine graphics, including wall patches, sprites, and all menu graphics, are stored in the WAD files in a special picture format. Notably excepted are the textures for floors and ceilings, which are known as flats.

A picture header gives its width and height, and offset values. Following the header are pointers to data for each column of pixels; the number of these pointers is equal to the picture width.

The data for each column is divided into posts, which are lines of colored pixels going downward on the screen. Each post is described by its starting height (relative to the top of the picture) and number of pixels, followed by a value for each of the pixels. Picture descriptions can (and do) skip over some pixel positions; these pixels are transparent. (Since transparent pixels are not changed when drawing a particular picture, whatever was drawn into the frame buffer previously will show through.)

Each pixel is given as an unsigned byte (and thus is valued from 0 to 255). The pixel value is first used as an index into the current COLORMAP, which gives a new pixel value (from 0 to 255) adjusted for the desired light level. (At full brightness, the pixel value is unchanged.) Then this new pixel value is written into the frame buffer. The actual red, green, and blue values corresponding to the palette index in the current palette are stored in the VGA graphics card's 8-bit hardware palette.

Note that gamma correction, a user-adjustable setting that can lighten the colors for dark-looking monitors, is handled when setting the game's palette and not when actually drawing the graphics themselves. This avoids an additional indirection.

The patch format originally (in the alpha and beta version) was more compact but limited. The alpha used bytes (instead of words) for dimension and starting offsets in the picture header. Both alpha and beta use words (instead of dwords) for locating column offsets, and neither features the dummy bytes that were added to the column spans in the final version.

For comparison, a table of the old and the new inserted in the patch_t struct from the Doom source code:

typedef struct
{
short origsize; // the orig size of "grabbed" gfx
short width; // bounding box size
short height;
short leftoffset; // pixels to the left of origin
short topoffset; // pixels above the origin
unsigned short collumnofs[320]; // only [width] used, the [0] is &collumnofs[width]
} patch_t;

Since the alpha version of the picture format used a byte for height and width, it did not support images larger than 255 pixels in any dimension. Other formats were therefore needed for wide graphics. The following formats have been used in alpha versions before being discarded:

Alpha raw-and-header: Uses a header similar to that of final patches (four shorts for width, height, and presumably left and top offsets) followed by a raw dump of pixel data technically identical to flats (one byte per pixel, row-major). Index 255 is transparent. This is used notably for the Doom v0.2 PLAYSCREEN lump.

GNUM: A simple raw dump of pixel data technically identical to flats, but with a size 10x12 pixels. This is used in Doom v0.4 for a series of lumps called GNUM0 to GNUM9.

Snea: Uses a two-byte header indicating the quarter of the width and the full height. This is followed by an interleaved, column-major dump of pixel data: columns are described in the sequence 0, 4, 8, etc., then 1, 5, 9, etc. and so on. This format was introduced as a replacement of the raw-and-header one in Doom v0.4, and remained until the Beta where it was still used for the TITLEPIC (but nothing else).

HUFONT: Uses a 770-byte header indicating first the character height on a short, then the character width of each 256 characters in sequence, each on a single byte, then the start offset of each character data on a short. Character data is in column-major format, color index 0 is transparent. This was used in Doom v0.4 and Doom v0.5 for the system font.

A centaur from Harmony. To the left, tall patches are not supported; to the right, they are.

A post starts with a two-byte header, the first byte containing the post's "topdelta", or its vertical offset from the top of the picture, and the second containing its length. This means that a post has severe limitations. Since the format uses a single byte to store a post's offset from the top of the image, if a picture is tall (more than 255 pixels high) it is possible that a post's topdelta becomes impossible to express normally. A topdelta of 255 is reserved to signal that there are no more posts in the column, and since it is stored in a single byte, a topdelta therefore cannot be larger than 254.

A workaround was designed, which is known as DeePsea tall patches. A post's topdelta is compared to the previous post's topdelta (or to -1 if there are no previous posts in the current column). If the new topdelta is less than the previous topdelta, it is interpreted as representing a tall patch, and the two values are added together, with the sum serving as the current post's offset. Since a post is normally supposed to go below its predecessor, this trick is usually non-ambiguous. However, if support for tall patches is not implemented, then these posts end up overwriting previous posts by being put at their literal offset, rather than the derived one.