GIF (Graphics Interchange Format) is an image file format, developed in 1987. It became popular in 1990s as one of the main image formats used in World Wide Web.
GIF format allows encoding of palette-based images up to 256 colors (each of the colors can be chosen from a 24-bit RGB colorspace). Image data stream uses LZW (Lempel-Ziv-Welch) lossless compression.
Over the years, several version of the format were published and several extensions to it were made, namely, a popular Netscape extension that allows to store several images in one file, switching between them, which produces crude form of animation.
Structurally, format consists of several mandatory headers and then a stream of blocks follows. Blocks can carry additional metainformation or image data.
This page hosts a formal specification of GIF (Graphics Interchange Format) image file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
digraph {
rankdir=LR;
node [shape=plaintext];
subgraph cluster__gif {
label="Gif";
graph[style=dotted];
gif__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="hdr_pos">0</TD><TD PORT="hdr_size">6</TD><TD>Header</TD><TD PORT="hdr_type">hdr</TD></TR>
<TR><TD PORT="logical_screen_descriptor_pos">6</TD><TD PORT="logical_screen_descriptor_size">7</TD><TD>LogicalScreenDescriptorStruct</TD><TD PORT="logical_screen_descriptor_type">logical_screen_descriptor</TD></TR>
<TR><TD PORT="global_color_table_pos">13</TD><TD PORT="global_color_table_size">(logical_screen_descriptor.color_table_size * 3)</TD><TD>ColorTable</TD><TD PORT="global_color_table_type">global_color_table</TD></TR>
<TR><TD PORT="blocks_pos">...</TD><TD PORT="blocks_size">...</TD><TD>Block</TD><TD PORT="blocks_type">blocks</TD></TR>
<TR><TD COLSPAN="4" PORT="blocks__repeat">repeat until ((_io.eof?) || (_.block_type == :block_type_end_of_file)) </TD></TR>
</TABLE>>];
subgraph cluster__image_data {
label="Gif::ImageData";
graph[style=dotted];
image_data__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="lzw_min_code_size_pos">0</TD><TD PORT="lzw_min_code_size_size">1</TD><TD>u1</TD><TD PORT="lzw_min_code_size_type">lzw_min_code_size</TD></TR>
<TR><TD PORT="subblocks_pos">1</TD><TD PORT="subblocks_size">...</TD><TD>Subblocks</TD><TD PORT="subblocks_type">subblocks</TD></TR>
</TABLE>>];
}
subgraph cluster__color_table_entry {
label="Gif::ColorTableEntry";
graph[style=dotted];
color_table_entry__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="red_pos">0</TD><TD PORT="red_size">1</TD><TD>u1</TD><TD PORT="red_type">red</TD></TR>
<TR><TD PORT="green_pos">1</TD><TD PORT="green_size">1</TD><TD>u1</TD><TD PORT="green_type">green</TD></TR>
<TR><TD PORT="blue_pos">2</TD><TD PORT="blue_size">1</TD><TD>u1</TD><TD PORT="blue_type">blue</TD></TR>
</TABLE>>];
}
subgraph cluster__logical_screen_descriptor_struct {
label="Gif::LogicalScreenDescriptorStruct";
graph[style=dotted];
logical_screen_descriptor_struct__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="screen_width_pos">0</TD><TD PORT="screen_width_size">2</TD><TD>u2le</TD><TD PORT="screen_width_type">screen_width</TD></TR>
<TR><TD PORT="screen_height_pos">2</TD><TD PORT="screen_height_size">2</TD><TD>u2le</TD><TD PORT="screen_height_type">screen_height</TD></TR>
<TR><TD PORT="flags_pos">4</TD><TD PORT="flags_size">1</TD><TD>u1</TD><TD PORT="flags_type">flags</TD></TR>
<TR><TD PORT="bg_color_index_pos">5</TD><TD PORT="bg_color_index_size">1</TD><TD>u1</TD><TD PORT="bg_color_index_type">bg_color_index</TD></TR>
<TR><TD PORT="pixel_aspect_ratio_pos">6</TD><TD PORT="pixel_aspect_ratio_size">1</TD><TD>u1</TD><TD PORT="pixel_aspect_ratio_type">pixel_aspect_ratio</TD></TR>
</TABLE>>];
logical_screen_descriptor_struct__inst__has_color_table [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>has_color_table</TD><TD>(flags & 128) != 0</TD></TR>
</TABLE>>];
logical_screen_descriptor_struct__inst__color_table_size [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>color_table_size</TD><TD>(2 << (flags & 7))</TD></TR>
</TABLE>>];
}
subgraph cluster__local_image_descriptor {
label="Gif::LocalImageDescriptor";
graph[style=dotted];
local_image_descriptor__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="left_pos">0</TD><TD PORT="left_size">2</TD><TD>u2le</TD><TD PORT="left_type">left</TD></TR>
<TR><TD PORT="top_pos">2</TD><TD PORT="top_size">2</TD><TD>u2le</TD><TD PORT="top_type">top</TD></TR>
<TR><TD PORT="width_pos">4</TD><TD PORT="width_size">2</TD><TD>u2le</TD><TD PORT="width_type">width</TD></TR>
<TR><TD PORT="height_pos">6</TD><TD PORT="height_size">2</TD><TD>u2le</TD><TD PORT="height_type">height</TD></TR>
<TR><TD PORT="flags_pos">8</TD><TD PORT="flags_size">1</TD><TD>u1</TD><TD PORT="flags_type">flags</TD></TR>
<TR><TD PORT="local_color_table_pos">9</TD><TD PORT="local_color_table_size">(color_table_size * 3)</TD><TD>ColorTable</TD><TD PORT="local_color_table_type">local_color_table</TD></TR>
<TR><TD PORT="image_data_pos">...</TD><TD PORT="image_data_size">...</TD><TD>ImageData</TD><TD PORT="image_data_type">image_data</TD></TR>
</TABLE>>];
local_image_descriptor__inst__has_color_table [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>has_color_table</TD><TD>(flags & 128) != 0</TD></TR>
</TABLE>>];
local_image_descriptor__inst__has_interlace [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>has_interlace</TD><TD>(flags & 64) != 0</TD></TR>
</TABLE>>];
local_image_descriptor__inst__has_sorted_color_table [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>has_sorted_color_table</TD><TD>(flags & 32) != 0</TD></TR>
</TABLE>>];
local_image_descriptor__inst__color_table_size [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>color_table_size</TD><TD>(2 << (flags & 7))</TD></TR>
</TABLE>>];
}
subgraph cluster__block {
label="Gif::Block";
graph[style=dotted];
block__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="block_type_pos">0</TD><TD PORT="block_type_size">1</TD><TD>u1→BlockType</TD><TD PORT="block_type_type">block_type</TD></TR>
<TR><TD PORT="body_pos">1</TD><TD PORT="body_size">...</TD><TD>switch (block_type)</TD><TD PORT="body_type">body</TD></TR>
</TABLE>>];
block__seq_body_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
<TR><TD>:block_type_extension</TD><TD PORT="case0">Extension</TD></TR>
<TR><TD>:block_type_local_image_descriptor</TD><TD PORT="case1">LocalImageDescriptor</TD></TR>
</TABLE>>];
}
subgraph cluster__color_table {
label="Gif::ColorTable";
graph[style=dotted];
color_table__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="entries_pos">0</TD><TD PORT="entries_size">3</TD><TD>ColorTableEntry</TD><TD PORT="entries_type">entries</TD></TR>
<TR><TD COLSPAN="4" PORT="entries__repeat">repeat to end of stream</TD></TR>
</TABLE>>];
}
subgraph cluster__header {
label="Gif::Header";
graph[style=dotted];
header__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="magic_pos">0</TD><TD PORT="magic_size">3</TD><TD></TD><TD PORT="magic_type">magic</TD></TR>
<TR><TD PORT="version_pos">3</TD><TD PORT="version_size">3</TD><TD>str(ASCII)</TD><TD PORT="version_type">version</TD></TR>
</TABLE>>];
}
subgraph cluster__ext_graphic_control {
label="Gif::ExtGraphicControl";
graph[style=dotted];
ext_graphic_control__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="block_size_pos">0</TD><TD PORT="block_size_size">1</TD><TD></TD><TD PORT="block_size_type">block_size</TD></TR>
<TR><TD PORT="flags_pos">1</TD><TD PORT="flags_size">1</TD><TD>u1</TD><TD PORT="flags_type">flags</TD></TR>
<TR><TD PORT="delay_time_pos">2</TD><TD PORT="delay_time_size">2</TD><TD>u2le</TD><TD PORT="delay_time_type">delay_time</TD></TR>
<TR><TD PORT="transparent_idx_pos">4</TD><TD PORT="transparent_idx_size">1</TD><TD>u1</TD><TD PORT="transparent_idx_type">transparent_idx</TD></TR>
<TR><TD PORT="terminator_pos">5</TD><TD PORT="terminator_size">1</TD><TD></TD><TD PORT="terminator_type">terminator</TD></TR>
</TABLE>>];
ext_graphic_control__inst__transparent_color_flag [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>transparent_color_flag</TD><TD>(flags & 1) != 0</TD></TR>
</TABLE>>];
ext_graphic_control__inst__user_input_flag [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>user_input_flag</TD><TD>(flags & 2) != 0</TD></TR>
</TABLE>>];
}
subgraph cluster__subblock {
label="Gif::Subblock";
graph[style=dotted];
subblock__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="len_bytes_pos">0</TD><TD PORT="len_bytes_size">1</TD><TD>u1</TD><TD PORT="len_bytes_type">len_bytes</TD></TR>
<TR><TD PORT="bytes_pos">1</TD><TD PORT="bytes_size">len_bytes</TD><TD></TD><TD PORT="bytes_type">bytes</TD></TR>
</TABLE>>];
}
subgraph cluster__application_id {
label="Gif::ApplicationId";
graph[style=dotted];
application_id__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="len_bytes_pos">0</TD><TD PORT="len_bytes_size">1</TD><TD>u1</TD><TD PORT="len_bytes_type">len_bytes</TD></TR>
<TR><TD PORT="application_identifier_pos">1</TD><TD PORT="application_identifier_size">8</TD><TD>str(ASCII)</TD><TD PORT="application_identifier_type">application_identifier</TD></TR>
<TR><TD PORT="application_auth_code_pos">9</TD><TD PORT="application_auth_code_size">3</TD><TD></TD><TD PORT="application_auth_code_type">application_auth_code</TD></TR>
</TABLE>>];
}
subgraph cluster__ext_application {
label="Gif::ExtApplication";
graph[style=dotted];
ext_application__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="application_id_pos">0</TD><TD PORT="application_id_size">12</TD><TD>ApplicationId</TD><TD PORT="application_id_type">application_id</TD></TR>
<TR><TD PORT="subblocks_pos">12</TD><TD PORT="subblocks_size">...</TD><TD>Subblock</TD><TD PORT="subblocks_type">subblocks</TD></TR>
<TR><TD COLSPAN="4" PORT="subblocks__repeat">repeat until _.len_bytes == 0</TD></TR>
</TABLE>>];
}
subgraph cluster__subblocks {
label="Gif::Subblocks";
graph[style=dotted];
subblocks__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="entries_pos">0</TD><TD PORT="entries_size">...</TD><TD>Subblock</TD><TD PORT="entries_type">entries</TD></TR>
<TR><TD COLSPAN="4" PORT="entries__repeat">repeat until _.len_bytes == 0</TD></TR>
</TABLE>>];
}
subgraph cluster__extension {
label="Gif::Extension";
graph[style=dotted];
extension__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="label_pos">0</TD><TD PORT="label_size">1</TD><TD>u1→ExtensionLabel</TD><TD PORT="label_type">label</TD></TR>
<TR><TD PORT="body_pos">1</TD><TD PORT="body_size">...</TD><TD>switch (label)</TD><TD PORT="body_type">body</TD></TR>
</TABLE>>];
extension__seq_body_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
<TR><TD>:extension_label_application</TD><TD PORT="case0">ExtApplication</TD></TR>
<TR><TD>:extension_label_comment</TD><TD PORT="case1">Subblocks</TD></TR>
<TR><TD>:extension_label_graphic_control</TD><TD PORT="case2">ExtGraphicControl</TD></TR>
<TR><TD>_</TD><TD PORT="case3">Subblocks</TD></TR>
</TABLE>>];
}
}
gif__seq:hdr_type -> header__seq [style=bold];
gif__seq:logical_screen_descriptor_type -> logical_screen_descriptor_struct__seq [style=bold];
logical_screen_descriptor_struct__inst__color_table_size:color_table_size_type -> gif__seq:global_color_table_size [color="#404040"];
gif__seq:global_color_table_type -> color_table__seq [style=bold];
gif__seq:blocks_type -> block__seq [style=bold];
block__seq:block_type_type -> gif__seq:blocks__repeat [color="#404040"];
image_data__seq:subblocks_type -> subblocks__seq [style=bold];
logical_screen_descriptor_struct__seq:flags_type -> logical_screen_descriptor_struct__inst__has_color_table [color="#404040"];
logical_screen_descriptor_struct__seq:flags_type -> logical_screen_descriptor_struct__inst__color_table_size [color="#404040"];
local_image_descriptor__inst__color_table_size:color_table_size_type -> local_image_descriptor__seq:local_color_table_size [color="#404040"];
local_image_descriptor__seq:local_color_table_type -> color_table__seq [style=bold];
local_image_descriptor__seq:image_data_type -> image_data__seq [style=bold];
local_image_descriptor__seq:flags_type -> local_image_descriptor__inst__has_color_table [color="#404040"];
local_image_descriptor__seq:flags_type -> local_image_descriptor__inst__has_interlace [color="#404040"];
local_image_descriptor__seq:flags_type -> local_image_descriptor__inst__has_sorted_color_table [color="#404040"];
local_image_descriptor__seq:flags_type -> local_image_descriptor__inst__color_table_size [color="#404040"];
block__seq:body_type -> block__seq_body_switch [style=bold];
block__seq_body_switch:case0 -> extension__seq [style=bold];
block__seq_body_switch:case1 -> local_image_descriptor__seq [style=bold];
block__seq:block_type_type -> block__seq:body_type [color="#404040"];
color_table__seq:entries_type -> color_table_entry__seq [style=bold];
ext_graphic_control__seq:flags_type -> ext_graphic_control__inst__transparent_color_flag [color="#404040"];
ext_graphic_control__seq:flags_type -> ext_graphic_control__inst__user_input_flag [color="#404040"];
subblock__seq:len_bytes_type -> subblock__seq:bytes_size [color="#404040"];
ext_application__seq:application_id_type -> application_id__seq [style=bold];
ext_application__seq:subblocks_type -> subblock__seq [style=bold];
subblock__seq:len_bytes_type -> ext_application__seq:subblocks__repeat [color="#404040"];
subblocks__seq:entries_type -> subblock__seq [style=bold];
subblock__seq:len_bytes_type -> subblocks__seq:entries__repeat [color="#404040"];
extension__seq:body_type -> extension__seq_body_switch [style=bold];
extension__seq_body_switch:case0 -> ext_application__seq [style=bold];
extension__seq_body_switch:case1 -> subblocks__seq [style=bold];
extension__seq_body_switch:case2 -> ext_graphic_control__seq [style=bold];
extension__seq_body_switch:case3 -> subblocks__seq [style=bold];
extension__seq:label_type -> extension__seq:body_type [color="#404040"];
}