Logical screen descriptor
Right after the header come 7 bytes describing the "logical screen": the area on which every image in the file is drawn. This block also says whether the file has a global color table and how large it is.
Structure
| Bytes | Field | Example |
|---|---|---|
| 0-1 | screen width (little-endian) | 04 00 = 4 |
| 2-3 | screen height | 02 00 = 2 |
| 4 | flags | F1 |
| 5 | background color index | 00 |
| 6 | pixel aspect ratio | 00 |
The flag byte
| N | Colors | Palette bytes | LZW minimum code size |
|---|---|---|---|
| 0 | 2 | 6 | 2 |
| 1 | 4 | 12 | 2 |
| 2 | 8 | 24 | 3 |
| 3 | 16 | 48 | 4 |
| 7 | 256 | 768 | 8 |
The palette always has a power-of-two number of entries. An image in 5 colors needs an 8-color palette with 3 entries unused. The last column belongs to the image block: for 2-color palettes the minimum code size is still 2, because GIF does not allow 1.
The color resolution field (bits 6-4) is informational and practically unused (see color resolution). The sort flag once helped decoders with fewer colors pick the most important ones.
Background and aspect
Background color is a global palette index used to fill the screen wherever no image reaches. Web browsers usually ignore it and show transparency there.
Pixel aspect: 0 means "not given"; any other value N gives (N + 15) / 64. See pixel aspect ratio.
Global color table
If bit 7 is set, the palette colors follow the screen descriptor directly: 3 bytes (R, G, B) per color, with no separators. In the example 4 colors take 12 bytes:
The global table applies to every image that has no local table of its own. The values carry no color space information, so in practice they are treated as sRGB.
function readScreen(b, pos) { // pos: right after the header
const w = b[pos] | (b[pos + 1] << 8), h = b[pos + 2] | (b[pos + 3] << 8);
const flags = b[pos + 4];
const hasGct = (flags & 0x80) !== 0;
const gctSize = 2 << (flags & 0x07); // 2^(N+1)
return { w, h, hasGct, gctSize, bg: b[pos + 5], aspect: b[pos + 6] };
}