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.
All parsing code for Java generated by Kaitai Struct depends on the Java runtime library. You have to install it before you can parse data.
The Java runtime library is published in the Maven Central Repository. Refer to the artifact page for instructions how to add it into your project with the build tool that you use.
Parse a local file and get structure in memory:
Gif data = Gif.fromFile("path/to/local/file.gif");
Or parse structure from a byte array:
byte[] someArray = new byte[] { ... };
Gif data = new Gif(new ByteBufferKaitaiStream(someArray));
After that, one can get various attributes from the structure by invoking getter methods like:
data.hdr() // => get hdr
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.nio.charset.Charset;
/**
* 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.
*/
public class Gif extends KaitaiStruct {
public static Gif fromFile(String fileName) throws IOException {
return new Gif(new ByteBufferKaitaiStream(fileName));
}
public enum BlockType {
EXTENSION(33),
LOCAL_IMAGE_DESCRIPTOR(44),
END_OF_FILE(59);
private final long id;
BlockType(long id) { this.id = id; }
public long id() { return id; }
private static final Map<Long, BlockType> byId = new HashMap<Long, BlockType>(3);
static {
for (BlockType e : BlockType.values())
byId.put(e.id(), e);
}
public static BlockType byId(long id) { return byId.get(id); }
}
public enum ExtensionLabel {
GRAPHIC_CONTROL(249),
COMMENT(254),
APPLICATION(255);
private final long id;
ExtensionLabel(long id) { this.id = id; }
public long id() { return id; }
private static final Map<Long, ExtensionLabel> byId = new HashMap<Long, ExtensionLabel>(3);
static {
for (ExtensionLabel e : ExtensionLabel.values())
byId.put(e.id(), e);
}
public static ExtensionLabel byId(long id) { return byId.get(id); }
}
public Gif(KaitaiStream _io) {
this(_io, null, null);
}
public Gif(KaitaiStream _io, KaitaiStruct _parent) {
this(_io, _parent, null);
}
public Gif(KaitaiStream _io, KaitaiStruct _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root == null ? this : _root;
_read();
}
private void _read() {
this.hdr = new Header(this._io, this, _root);
this.logicalScreenDescriptor = new LogicalScreenDescriptorStruct(this._io, this, _root);
if (logicalScreenDescriptor().hasColorTable()) {
this._raw_globalColorTable = this._io.readBytes((logicalScreenDescriptor().colorTableSize() * 3));
KaitaiStream _io__raw_globalColorTable = new ByteBufferKaitaiStream(_raw_globalColorTable);
this.globalColorTable = new ColorTable(_io__raw_globalColorTable, this, _root);
}
this.blocks = new ArrayList<Block>();
{
Block _it;
int i = 0;
do {
_it = new Block(this._io, this, _root);
this.blocks.add(_it);
i++;
} while (!( ((_io().isEof()) || (_it.blockType() == BlockType.END_OF_FILE)) ));
}
}
/**
* @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 22</a>
*/
public static class ImageData extends KaitaiStruct {
public static ImageData fromFile(String fileName) throws IOException {
return new ImageData(new ByteBufferKaitaiStream(fileName));
}
public ImageData(KaitaiStream _io) {
this(_io, null, null);
}
public ImageData(KaitaiStream _io, Gif.LocalImageDescriptor _parent) {
this(_io, _parent, null);
}
public ImageData(KaitaiStream _io, Gif.LocalImageDescriptor _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.lzwMinCodeSize = this._io.readU1();
this.subblocks = new Subblocks(this._io, this, _root);
}
private int lzwMinCodeSize;
private Subblocks subblocks;
private Gif _root;
private Gif.LocalImageDescriptor _parent;
public int lzwMinCodeSize() { return lzwMinCodeSize; }
public Subblocks subblocks() { return subblocks; }
public Gif _root() { return _root; }
public Gif.LocalImageDescriptor _parent() { return _parent; }
}
public static class ColorTableEntry extends KaitaiStruct {
public static ColorTableEntry fromFile(String fileName) throws IOException {
return new ColorTableEntry(new ByteBufferKaitaiStream(fileName));
}
public ColorTableEntry(KaitaiStream _io) {
this(_io, null, null);
}
public ColorTableEntry(KaitaiStream _io, Gif.ColorTable _parent) {
this(_io, _parent, null);
}
public ColorTableEntry(KaitaiStream _io, Gif.ColorTable _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.red = this._io.readU1();
this.green = this._io.readU1();
this.blue = this._io.readU1();
}
private int red;
private int green;
private int blue;
private Gif _root;
private Gif.ColorTable _parent;
public int red() { return red; }
public int green() { return green; }
public int blue() { return blue; }
public Gif _root() { return _root; }
public Gif.ColorTable _parent() { return _parent; }
}
/**
* @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 18</a>
*/
public static class LogicalScreenDescriptorStruct extends KaitaiStruct {
public static LogicalScreenDescriptorStruct fromFile(String fileName) throws IOException {
return new LogicalScreenDescriptorStruct(new ByteBufferKaitaiStream(fileName));
}
public LogicalScreenDescriptorStruct(KaitaiStream _io) {
this(_io, null, null);
}
public LogicalScreenDescriptorStruct(KaitaiStream _io, Gif _parent) {
this(_io, _parent, null);
}
public LogicalScreenDescriptorStruct(KaitaiStream _io, Gif _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.screenWidth = this._io.readU2le();
this.screenHeight = this._io.readU2le();
this.flags = this._io.readU1();
this.bgColorIndex = this._io.readU1();
this.pixelAspectRatio = this._io.readU1();
}
private Boolean hasColorTable;
public Boolean hasColorTable() {
if (this.hasColorTable != null)
return this.hasColorTable;
boolean _tmp = (boolean) ((flags() & 128) != 0);
this.hasColorTable = _tmp;
return this.hasColorTable;
}
private Integer colorTableSize;
public Integer colorTableSize() {
if (this.colorTableSize != null)
return this.colorTableSize;
int _tmp = (int) ((2 << (flags() & 7)));
this.colorTableSize = _tmp;
return this.colorTableSize;
}
private int screenWidth;
private int screenHeight;
private int flags;
private int bgColorIndex;
private int pixelAspectRatio;
private Gif _root;
private Gif _parent;
public int screenWidth() { return screenWidth; }
public int screenHeight() { return screenHeight; }
public int flags() { return flags; }
public int bgColorIndex() { return bgColorIndex; }
public int pixelAspectRatio() { return pixelAspectRatio; }
public Gif _root() { return _root; }
public Gif _parent() { return _parent; }
}
public static class LocalImageDescriptor extends KaitaiStruct {
public static LocalImageDescriptor fromFile(String fileName) throws IOException {
return new LocalImageDescriptor(new ByteBufferKaitaiStream(fileName));
}
public LocalImageDescriptor(KaitaiStream _io) {
this(_io, null, null);
}
public LocalImageDescriptor(KaitaiStream _io, Gif.Block _parent) {
this(_io, _parent, null);
}
public LocalImageDescriptor(KaitaiStream _io, Gif.Block _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.left = this._io.readU2le();
this.top = this._io.readU2le();
this.width = this._io.readU2le();
this.height = this._io.readU2le();
this.flags = this._io.readU1();
if (hasColorTable()) {
this._raw_localColorTable = this._io.readBytes((colorTableSize() * 3));
KaitaiStream _io__raw_localColorTable = new ByteBufferKaitaiStream(_raw_localColorTable);
this.localColorTable = new ColorTable(_io__raw_localColorTable, this, _root);
}
this.imageData = new ImageData(this._io, this, _root);
}
private Boolean hasColorTable;
public Boolean hasColorTable() {
if (this.hasColorTable != null)
return this.hasColorTable;
boolean _tmp = (boolean) ((flags() & 128) != 0);
this.hasColorTable = _tmp;
return this.hasColorTable;
}
private Boolean hasInterlace;
public Boolean hasInterlace() {
if (this.hasInterlace != null)
return this.hasInterlace;
boolean _tmp = (boolean) ((flags() & 64) != 0);
this.hasInterlace = _tmp;
return this.hasInterlace;
}
private Boolean hasSortedColorTable;
public Boolean hasSortedColorTable() {
if (this.hasSortedColorTable != null)
return this.hasSortedColorTable;
boolean _tmp = (boolean) ((flags() & 32) != 0);
this.hasSortedColorTable = _tmp;
return this.hasSortedColorTable;
}
private Integer colorTableSize;
public Integer colorTableSize() {
if (this.colorTableSize != null)
return this.colorTableSize;
int _tmp = (int) ((2 << (flags() & 7)));
this.colorTableSize = _tmp;
return this.colorTableSize;
}
private int left;
private int top;
private int width;
private int height;
private int flags;
private ColorTable localColorTable;
private ImageData imageData;
private Gif _root;
private Gif.Block _parent;
private byte[] _raw_localColorTable;
public int left() { return left; }
public int top() { return top; }
public int width() { return width; }
public int height() { return height; }
public int flags() { return flags; }
public ColorTable localColorTable() { return localColorTable; }
public ImageData imageData() { return imageData; }
public Gif _root() { return _root; }
public Gif.Block _parent() { return _parent; }
public byte[] _raw_localColorTable() { return _raw_localColorTable; }
}
public static class Block extends KaitaiStruct {
public static Block fromFile(String fileName) throws IOException {
return new Block(new ByteBufferKaitaiStream(fileName));
}
public Block(KaitaiStream _io) {
this(_io, null, null);
}
public Block(KaitaiStream _io, Gif _parent) {
this(_io, _parent, null);
}
public Block(KaitaiStream _io, Gif _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.blockType = Gif.BlockType.byId(this._io.readU1());
{
BlockType on = blockType();
if (on != null) {
switch (blockType()) {
case EXTENSION: {
this.body = new Extension(this._io, this, _root);
break;
}
case LOCAL_IMAGE_DESCRIPTOR: {
this.body = new LocalImageDescriptor(this._io, this, _root);
break;
}
}
}
}
}
private BlockType blockType;
private KaitaiStruct body;
private Gif _root;
private Gif _parent;
public BlockType blockType() { return blockType; }
public KaitaiStruct body() { return body; }
public Gif _root() { return _root; }
public Gif _parent() { return _parent; }
}
/**
* @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 19</a>
*/
public static class ColorTable extends KaitaiStruct {
public static ColorTable fromFile(String fileName) throws IOException {
return new ColorTable(new ByteBufferKaitaiStream(fileName));
}
public ColorTable(KaitaiStream _io) {
this(_io, null, null);
}
public ColorTable(KaitaiStream _io, KaitaiStruct _parent) {
this(_io, _parent, null);
}
public ColorTable(KaitaiStream _io, KaitaiStruct _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.entries = new ArrayList<ColorTableEntry>();
{
int i = 0;
while (!this._io.isEof()) {
this.entries.add(new ColorTableEntry(this._io, this, _root));
i++;
}
}
}
private ArrayList<ColorTableEntry> entries;
private Gif _root;
private KaitaiStruct _parent;
public ArrayList<ColorTableEntry> entries() { return entries; }
public Gif _root() { return _root; }
public KaitaiStruct _parent() { return _parent; }
}
/**
* @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 17</a>
*/
public static class Header extends KaitaiStruct {
public static Header fromFile(String fileName) throws IOException {
return new Header(new ByteBufferKaitaiStream(fileName));
}
public Header(KaitaiStream _io) {
this(_io, null, null);
}
public Header(KaitaiStream _io, Gif _parent) {
this(_io, _parent, null);
}
public Header(KaitaiStream _io, Gif _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.magic = this._io.readBytes(3);
if (!(Arrays.equals(magic(), new byte[] { 71, 73, 70 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 71, 73, 70 }, magic(), _io(), "/types/header/seq/0");
}
this.version = new String(this._io.readBytes(3), Charset.forName("ASCII"));
}
private byte[] magic;
private String version;
private Gif _root;
private Gif _parent;
public byte[] magic() { return magic; }
public String version() { return version; }
public Gif _root() { return _root; }
public Gif _parent() { return _parent; }
}
/**
* @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 23</a>
*/
public static class ExtGraphicControl extends KaitaiStruct {
public static ExtGraphicControl fromFile(String fileName) throws IOException {
return new ExtGraphicControl(new ByteBufferKaitaiStream(fileName));
}
public ExtGraphicControl(KaitaiStream _io) {
this(_io, null, null);
}
public ExtGraphicControl(KaitaiStream _io, Gif.Extension _parent) {
this(_io, _parent, null);
}
public ExtGraphicControl(KaitaiStream _io, Gif.Extension _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.blockSize = this._io.readBytes(1);
if (!(Arrays.equals(blockSize(), new byte[] { 4 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 4 }, blockSize(), _io(), "/types/ext_graphic_control/seq/0");
}
this.flags = this._io.readU1();
this.delayTime = this._io.readU2le();
this.transparentIdx = this._io.readU1();
this.terminator = this._io.readBytes(1);
if (!(Arrays.equals(terminator(), new byte[] { 0 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 0 }, terminator(), _io(), "/types/ext_graphic_control/seq/4");
}
}
private Boolean transparentColorFlag;
public Boolean transparentColorFlag() {
if (this.transparentColorFlag != null)
return this.transparentColorFlag;
boolean _tmp = (boolean) ((flags() & 1) != 0);
this.transparentColorFlag = _tmp;
return this.transparentColorFlag;
}
private Boolean userInputFlag;
public Boolean userInputFlag() {
if (this.userInputFlag != null)
return this.userInputFlag;
boolean _tmp = (boolean) ((flags() & 2) != 0);
this.userInputFlag = _tmp;
return this.userInputFlag;
}
private byte[] blockSize;
private int flags;
private int delayTime;
private int transparentIdx;
private byte[] terminator;
private Gif _root;
private Gif.Extension _parent;
public byte[] blockSize() { return blockSize; }
public int flags() { return flags; }
public int delayTime() { return delayTime; }
public int transparentIdx() { return transparentIdx; }
public byte[] terminator() { return terminator; }
public Gif _root() { return _root; }
public Gif.Extension _parent() { return _parent; }
}
public static class Subblock extends KaitaiStruct {
public static Subblock fromFile(String fileName) throws IOException {
return new Subblock(new ByteBufferKaitaiStream(fileName));
}
public Subblock(KaitaiStream _io) {
this(_io, null, null);
}
public Subblock(KaitaiStream _io, KaitaiStruct _parent) {
this(_io, _parent, null);
}
public Subblock(KaitaiStream _io, KaitaiStruct _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.lenBytes = this._io.readU1();
this.bytes = this._io.readBytes(lenBytes());
}
private int lenBytes;
private byte[] bytes;
private Gif _root;
private KaitaiStruct _parent;
public int lenBytes() { return lenBytes; }
public byte[] bytes() { return bytes; }
public Gif _root() { return _root; }
public KaitaiStruct _parent() { return _parent; }
}
public static class ApplicationId extends KaitaiStruct {
public static ApplicationId fromFile(String fileName) throws IOException {
return new ApplicationId(new ByteBufferKaitaiStream(fileName));
}
public ApplicationId(KaitaiStream _io) {
this(_io, null, null);
}
public ApplicationId(KaitaiStream _io, Gif.ExtApplication _parent) {
this(_io, _parent, null);
}
public ApplicationId(KaitaiStream _io, Gif.ExtApplication _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.lenBytes = this._io.readU1();
if (!(lenBytes() == 11)) {
throw new KaitaiStream.ValidationNotEqualError(11, lenBytes(), _io(), "/types/application_id/seq/0");
}
this.applicationIdentifier = new String(this._io.readBytes(8), Charset.forName("ASCII"));
this.applicationAuthCode = this._io.readBytes(3);
}
private int lenBytes;
private String applicationIdentifier;
private byte[] applicationAuthCode;
private Gif _root;
private Gif.ExtApplication _parent;
public int lenBytes() { return lenBytes; }
public String applicationIdentifier() { return applicationIdentifier; }
public byte[] applicationAuthCode() { return applicationAuthCode; }
public Gif _root() { return _root; }
public Gif.ExtApplication _parent() { return _parent; }
}
public static class ExtApplication extends KaitaiStruct {
public static ExtApplication fromFile(String fileName) throws IOException {
return new ExtApplication(new ByteBufferKaitaiStream(fileName));
}
public ExtApplication(KaitaiStream _io) {
this(_io, null, null);
}
public ExtApplication(KaitaiStream _io, Gif.Extension _parent) {
this(_io, _parent, null);
}
public ExtApplication(KaitaiStream _io, Gif.Extension _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.applicationId = new ApplicationId(this._io, this, _root);
this.subblocks = new ArrayList<Subblock>();
{
Subblock _it;
int i = 0;
do {
_it = new Subblock(this._io, this, _root);
this.subblocks.add(_it);
i++;
} while (!(_it.lenBytes() == 0));
}
}
private ApplicationId applicationId;
private ArrayList<Subblock> subblocks;
private Gif _root;
private Gif.Extension _parent;
public ApplicationId applicationId() { return applicationId; }
public ArrayList<Subblock> subblocks() { return subblocks; }
public Gif _root() { return _root; }
public Gif.Extension _parent() { return _parent; }
}
public static class Subblocks extends KaitaiStruct {
public static Subblocks fromFile(String fileName) throws IOException {
return new Subblocks(new ByteBufferKaitaiStream(fileName));
}
public Subblocks(KaitaiStream _io) {
this(_io, null, null);
}
public Subblocks(KaitaiStream _io, KaitaiStruct _parent) {
this(_io, _parent, null);
}
public Subblocks(KaitaiStream _io, KaitaiStruct _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.entries = new ArrayList<Subblock>();
{
Subblock _it;
int i = 0;
do {
_it = new Subblock(this._io, this, _root);
this.entries.add(_it);
i++;
} while (!(_it.lenBytes() == 0));
}
}
private ArrayList<Subblock> entries;
private Gif _root;
private KaitaiStruct _parent;
public ArrayList<Subblock> entries() { return entries; }
public Gif _root() { return _root; }
public KaitaiStruct _parent() { return _parent; }
}
public static class Extension extends KaitaiStruct {
public static Extension fromFile(String fileName) throws IOException {
return new Extension(new ByteBufferKaitaiStream(fileName));
}
public Extension(KaitaiStream _io) {
this(_io, null, null);
}
public Extension(KaitaiStream _io, Gif.Block _parent) {
this(_io, _parent, null);
}
public Extension(KaitaiStream _io, Gif.Block _parent, Gif _root) {
super(_io);
this._parent = _parent;
this._root = _root;
_read();
}
private void _read() {
this.label = Gif.ExtensionLabel.byId(this._io.readU1());
{
ExtensionLabel on = label();
if (on != null) {
switch (label()) {
case APPLICATION: {
this.body = new ExtApplication(this._io, this, _root);
break;
}
case COMMENT: {
this.body = new Subblocks(this._io, this, _root);
break;
}
case GRAPHIC_CONTROL: {
this.body = new ExtGraphicControl(this._io, this, _root);
break;
}
default: {
this.body = new Subblocks(this._io, this, _root);
break;
}
}
} else {
this.body = new Subblocks(this._io, this, _root);
}
}
}
private ExtensionLabel label;
private KaitaiStruct body;
private Gif _root;
private Gif.Block _parent;
public ExtensionLabel label() { return label; }
public KaitaiStruct body() { return body; }
public Gif _root() { return _root; }
public Gif.Block _parent() { return _parent; }
}
private Header hdr;
private LogicalScreenDescriptorStruct logicalScreenDescriptor;
private ColorTable globalColorTable;
private ArrayList<Block> blocks;
private Gif _root;
private KaitaiStruct _parent;
private byte[] _raw_globalColorTable;
public Header hdr() { return hdr; }
public LogicalScreenDescriptorStruct logicalScreenDescriptor() { return logicalScreenDescriptor; }
/**
* @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 18</a>
*/
public ColorTable globalColorTable() { return globalColorTable; }
public ArrayList<Block> blocks() { return blocks; }
public Gif _root() { return _root; }
public KaitaiStruct _parent() { return _parent; }
public byte[] _raw_globalColorTable() { return _raw_globalColorTable; }
}