GIF Parser: Extension Block vs Image Separator

This article explains how a Graphics Interchange Format (GIF) parser distinguishes between an extension block marker and an image separator byte. It covers the stream-level sentinel byte values defined in the GIF specifications (GIF87a and GIF89a), the sequential state machine used during parsing, and how the parser maintains byte alignment across variable-length sub-blocks to prevent misinterpreting data payloads as block markers.

A GIF parser distinguishes between an extension block and an image block by reading a single-byte sentinel value at the root level of the data stream. After reading the file header, the Logical Screen Descriptor, and any optional Global Color Table, the parser enters a loop expecting one of three valid structural markers:

Because 0x21 and 0x2C are distinct binary values, the parser identifies the incoming block type through a straightforward conditional check on this single byte.

State Tracking and Stream Synchronization

Distinguishing these markers relies on parser state synchronization. The parser only evaluates sentinel bytes when it is outside of any encapsulated data block. Once a sentinel is encountered, the parser follows specific structural rules defined for that block before looking for the next sentinel:

When 0x21 (Extension Introducer) Is Read:

  1. The parser reads the very next byte, which is the Extension Label (for example, 0xF9 for Graphic Control or 0xFF for Application).
  2. The parser then processes the extension payload, which is structured as a series of data sub-blocks. Each sub-block begins with a length byte (1 to 255) specifying the size of that chunk.
  3. The parser consumes data chunks sequentially until it encounters a Block Terminator byte of value 0x00 (a sub-block of length zero).
  4. Once the terminator is read, the extension is complete, and the parser returns to the root state, ready to read the next marker byte.

When 0x2C (Image Separator) Is Read:

  1. The parser reads the fixed 9-byte Image Descriptor containing boundary coordinates (Left, Top, Width, Height) and packed field flags.
  2. If the packed field indicates a Local Color Table, the parser reads the specified number of RGB palette bytes.
  3. The parser reads one byte representing the LZW Minimum Code Size.
  4. The parser then reads the image raster data, which is also organized as length-prefixed data sub-blocks ending with a 0x00 Block Terminator.
  5. Once the zero-byte terminator is consumed, the image block is fully processed, returning the parser to the root state to look for the next marker.

By enforcing length-based sub-block encapsulation, image data and extension metadata—even if they contain the raw byte values 0x21 or 0x2C—are skipped as raw payload. The parser only interprets 0x21 and 0x2C as structural indicators when it has cleanly exited the previous block's terminating sequence.