GIF (Graphics Interchange Format) image file: Go parsing library

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.

Go source code to parse GIF (Graphics Interchange Format) image file

gif.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"bytes"
)


/**
 * 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.
 */

type Gif_BlockType int
const (
	Gif_BlockType__Extension Gif_BlockType = 33
	Gif_BlockType__LocalImageDescriptor Gif_BlockType = 44
	Gif_BlockType__EndOfFile Gif_BlockType = 59
)
var values_Gif_BlockType = map[Gif_BlockType]struct{}{33: {}, 44: {}, 59: {}}
func (v Gif_BlockType) isDefined() bool {
	_, ok := values_Gif_BlockType[v]
	return ok
}

type Gif_ExtensionLabel int
const (
	Gif_ExtensionLabel__GraphicControl Gif_ExtensionLabel = 249
	Gif_ExtensionLabel__Comment Gif_ExtensionLabel = 254
	Gif_ExtensionLabel__Application Gif_ExtensionLabel = 255
)
var values_Gif_ExtensionLabel = map[Gif_ExtensionLabel]struct{}{249: {}, 254: {}, 255: {}}
func (v Gif_ExtensionLabel) isDefined() bool {
	_, ok := values_Gif_ExtensionLabel[v]
	return ok
}
type Gif struct {
	Hdr *Gif_Header
	LogicalScreenDescriptor *Gif_LogicalScreenDescriptorStruct
	GlobalColorTable *Gif_ColorTable
	Blocks []*Gif_Block
	_io *kaitai.Stream
	_root *Gif
	_parent kaitai.Struct
	_raw_GlobalColorTable []byte
}
func NewGif() *Gif {
	return &Gif{
	}
}

func (this Gif) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif) Read(io *kaitai.Stream, parent kaitai.Struct, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewGif_Header()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Hdr = tmp1
	tmp2 := NewGif_LogicalScreenDescriptorStruct()
	err = tmp2.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.LogicalScreenDescriptor = tmp2
	tmp3, err := this.LogicalScreenDescriptor.HasColorTable()
	if err != nil {
		return err
	}
	if (tmp3) {
		tmp4, err := this.LogicalScreenDescriptor.ColorTableSize()
		if err != nil {
			return err
		}
		tmp5, err := this._io.ReadBytes(int(tmp4 * 3))
		if err != nil {
			return err
		}
		tmp5 = tmp5
		this._raw_GlobalColorTable = tmp5
		_io__raw_GlobalColorTable := kaitai.NewStream(bytes.NewReader(this._raw_GlobalColorTable))
		tmp6 := NewGif_ColorTable()
		err = tmp6.Read(_io__raw_GlobalColorTable, this, this._root)
		if err != nil {
			return err
		}
		this.GlobalColorTable = tmp6
	}
	for i := 1;; i++ {
		tmp7 := NewGif_Block()
		err = tmp7.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		_it := tmp7
		this.Blocks = append(this.Blocks, _it)
		tmp8, err := this._io.EOF()
		if err != nil {
			return err
		}
		if  ((tmp8) || (_it.BlockType == Gif_BlockType__EndOfFile))  {
			break
		}
	}
	return err
}

/**
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 18</a>
 */
type Gif_ApplicationId struct {
	LenBytes uint8
	ApplicationIdentifier string
	ApplicationAuthCode []byte
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_ExtApplication
}
func NewGif_ApplicationId() *Gif_ApplicationId {
	return &Gif_ApplicationId{
	}
}

func (this Gif_ApplicationId) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_ApplicationId) Read(io *kaitai.Stream, parent *Gif_ExtApplication, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp9, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.LenBytes = tmp9
	if !(this.LenBytes == 11) {
		return kaitai.NewValidationNotEqualError(11, this.LenBytes, this._io, "/types/application_id/seq/0")
	}
	tmp10, err := this._io.ReadBytes(int(8))
	if err != nil {
		return err
	}
	tmp10 = tmp10
	this.ApplicationIdentifier = string(tmp10)
	tmp11, err := this._io.ReadBytes(int(3))
	if err != nil {
		return err
	}
	tmp11 = tmp11
	this.ApplicationAuthCode = tmp11
	return err
}
type Gif_Block struct {
	BlockType Gif_BlockType
	Body kaitai.Struct
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif
}
func NewGif_Block() *Gif_Block {
	return &Gif_Block{
	}
}

func (this Gif_Block) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_Block) Read(io *kaitai.Stream, parent *Gif, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp12, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.BlockType = Gif_BlockType(tmp12)
	switch (this.BlockType) {
	case Gif_BlockType__Extension:
		tmp13 := NewGif_Extension()
		err = tmp13.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp13
	case Gif_BlockType__LocalImageDescriptor:
		tmp14 := NewGif_LocalImageDescriptor()
		err = tmp14.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp14
	}
	return err
}

/**
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 19</a>
 */
type Gif_ColorTable struct {
	Entries []*Gif_ColorTableEntry
	_io *kaitai.Stream
	_root *Gif
	_parent kaitai.Struct
}
func NewGif_ColorTable() *Gif_ColorTable {
	return &Gif_ColorTable{
	}
}

func (this Gif_ColorTable) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_ColorTable) Read(io *kaitai.Stream, parent kaitai.Struct, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 0;; i++ {
		tmp15, err := this._io.EOF()
		if err != nil {
			return err
		}
		if tmp15 {
			break
		}
		tmp16 := NewGif_ColorTableEntry()
		err = tmp16.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Entries = append(this.Entries, tmp16)
	}
	return err
}
type Gif_ColorTableEntry struct {
	Red uint8
	Green uint8
	Blue uint8
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_ColorTable
}
func NewGif_ColorTableEntry() *Gif_ColorTableEntry {
	return &Gif_ColorTableEntry{
	}
}

func (this Gif_ColorTableEntry) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_ColorTableEntry) Read(io *kaitai.Stream, parent *Gif_ColorTable, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp17, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Red = tmp17
	tmp18, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Green = tmp18
	tmp19, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Blue = tmp19
	return err
}
type Gif_ExtApplication struct {
	ApplicationId *Gif_ApplicationId
	Subblocks []*Gif_Subblock
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_Extension
}
func NewGif_ExtApplication() *Gif_ExtApplication {
	return &Gif_ExtApplication{
	}
}

func (this Gif_ExtApplication) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_ExtApplication) Read(io *kaitai.Stream, parent *Gif_Extension, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp20 := NewGif_ApplicationId()
	err = tmp20.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.ApplicationId = tmp20
	for i := 1;; i++ {
		tmp21 := NewGif_Subblock()
		err = tmp21.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		_it := tmp21
		this.Subblocks = append(this.Subblocks, _it)
		if _it.LenBytes == 0 {
			break
		}
	}
	return err
}

/**
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 23</a>
 */
type Gif_ExtGraphicControl struct {
	BlockSize []byte
	Flags uint8
	DelayTime uint16
	TransparentIdx uint8
	Terminator []byte
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_Extension
	_f_transparentColorFlag bool
	transparentColorFlag bool
	_f_userInputFlag bool
	userInputFlag bool
}
func NewGif_ExtGraphicControl() *Gif_ExtGraphicControl {
	return &Gif_ExtGraphicControl{
	}
}

func (this Gif_ExtGraphicControl) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_ExtGraphicControl) Read(io *kaitai.Stream, parent *Gif_Extension, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp22, err := this._io.ReadBytes(int(1))
	if err != nil {
		return err
	}
	tmp22 = tmp22
	this.BlockSize = tmp22
	if !(bytes.Equal(this.BlockSize, []uint8{4})) {
		return kaitai.NewValidationNotEqualError([]uint8{4}, this.BlockSize, this._io, "/types/ext_graphic_control/seq/0")
	}
	tmp23, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Flags = tmp23
	tmp24, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.DelayTime = uint16(tmp24)
	tmp25, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.TransparentIdx = tmp25
	tmp26, err := this._io.ReadBytes(int(1))
	if err != nil {
		return err
	}
	tmp26 = tmp26
	this.Terminator = tmp26
	if !(bytes.Equal(this.Terminator, []uint8{0})) {
		return kaitai.NewValidationNotEqualError([]uint8{0}, this.Terminator, this._io, "/types/ext_graphic_control/seq/4")
	}
	return err
}
func (this *Gif_ExtGraphicControl) TransparentColorFlag() (v bool, err error) {
	if (this._f_transparentColorFlag) {
		return this.transparentColorFlag, nil
	}
	this._f_transparentColorFlag = true
	this.transparentColorFlag = bool(this.Flags & 1 != 0)
	return this.transparentColorFlag, nil
}
func (this *Gif_ExtGraphicControl) UserInputFlag() (v bool, err error) {
	if (this._f_userInputFlag) {
		return this.userInputFlag, nil
	}
	this._f_userInputFlag = true
	this.userInputFlag = bool(this.Flags & 2 != 0)
	return this.userInputFlag, nil
}
type Gif_Extension struct {
	Label Gif_ExtensionLabel
	Body kaitai.Struct
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_Block
}
func NewGif_Extension() *Gif_Extension {
	return &Gif_Extension{
	}
}

func (this Gif_Extension) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_Extension) Read(io *kaitai.Stream, parent *Gif_Block, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp27, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Label = Gif_ExtensionLabel(tmp27)
	switch (this.Label) {
	case Gif_ExtensionLabel__Application:
		tmp28 := NewGif_ExtApplication()
		err = tmp28.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp28
	case Gif_ExtensionLabel__Comment:
		tmp29 := NewGif_Subblocks()
		err = tmp29.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp29
	case Gif_ExtensionLabel__GraphicControl:
		tmp30 := NewGif_ExtGraphicControl()
		err = tmp30.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp30
	default:
		tmp31 := NewGif_Subblocks()
		err = tmp31.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp31
	}
	return err
}

/**
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 17</a>
 */
type Gif_Header struct {
	Magic []byte
	Version string
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif
}
func NewGif_Header() *Gif_Header {
	return &Gif_Header{
	}
}

func (this Gif_Header) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_Header) Read(io *kaitai.Stream, parent *Gif, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp32, err := this._io.ReadBytes(int(3))
	if err != nil {
		return err
	}
	tmp32 = tmp32
	this.Magic = tmp32
	if !(bytes.Equal(this.Magic, []uint8{71, 73, 70})) {
		return kaitai.NewValidationNotEqualError([]uint8{71, 73, 70}, this.Magic, this._io, "/types/header/seq/0")
	}
	tmp33, err := this._io.ReadBytes(int(3))
	if err != nil {
		return err
	}
	tmp33 = tmp33
	this.Version = string(tmp33)
	return err
}

/**
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 22</a>
 */
type Gif_ImageData struct {
	LzwMinCodeSize uint8
	Subblocks *Gif_Subblocks
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_LocalImageDescriptor
}
func NewGif_ImageData() *Gif_ImageData {
	return &Gif_ImageData{
	}
}

func (this Gif_ImageData) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_ImageData) Read(io *kaitai.Stream, parent *Gif_LocalImageDescriptor, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp34, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.LzwMinCodeSize = tmp34
	tmp35 := NewGif_Subblocks()
	err = tmp35.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Subblocks = tmp35
	return err
}
type Gif_LocalImageDescriptor struct {
	Left uint16
	Top uint16
	Width uint16
	Height uint16
	Flags uint8
	LocalColorTable *Gif_ColorTable
	ImageData *Gif_ImageData
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif_Block
	_raw_LocalColorTable []byte
	_f_colorTableSize bool
	colorTableSize int
	_f_hasColorTable bool
	hasColorTable bool
	_f_hasInterlace bool
	hasInterlace bool
	_f_hasSortedColorTable bool
	hasSortedColorTable bool
}
func NewGif_LocalImageDescriptor() *Gif_LocalImageDescriptor {
	return &Gif_LocalImageDescriptor{
	}
}

func (this Gif_LocalImageDescriptor) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_LocalImageDescriptor) Read(io *kaitai.Stream, parent *Gif_Block, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp36, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Left = uint16(tmp36)
	tmp37, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Top = uint16(tmp37)
	tmp38, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Width = uint16(tmp38)
	tmp39, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Height = uint16(tmp39)
	tmp40, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Flags = tmp40
	tmp41, err := this.HasColorTable()
	if err != nil {
		return err
	}
	if (tmp41) {
		tmp42, err := this.ColorTableSize()
		if err != nil {
			return err
		}
		tmp43, err := this._io.ReadBytes(int(tmp42 * 3))
		if err != nil {
			return err
		}
		tmp43 = tmp43
		this._raw_LocalColorTable = tmp43
		_io__raw_LocalColorTable := kaitai.NewStream(bytes.NewReader(this._raw_LocalColorTable))
		tmp44 := NewGif_ColorTable()
		err = tmp44.Read(_io__raw_LocalColorTable, this, this._root)
		if err != nil {
			return err
		}
		this.LocalColorTable = tmp44
	}
	tmp45 := NewGif_ImageData()
	err = tmp45.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.ImageData = tmp45
	return err
}
func (this *Gif_LocalImageDescriptor) ColorTableSize() (v int, err error) {
	if (this._f_colorTableSize) {
		return this.colorTableSize, nil
	}
	this._f_colorTableSize = true
	this.colorTableSize = int(2 << (this.Flags & 7))
	return this.colorTableSize, nil
}
func (this *Gif_LocalImageDescriptor) HasColorTable() (v bool, err error) {
	if (this._f_hasColorTable) {
		return this.hasColorTable, nil
	}
	this._f_hasColorTable = true
	this.hasColorTable = bool(this.Flags & 128 != 0)
	return this.hasColorTable, nil
}
func (this *Gif_LocalImageDescriptor) HasInterlace() (v bool, err error) {
	if (this._f_hasInterlace) {
		return this.hasInterlace, nil
	}
	this._f_hasInterlace = true
	this.hasInterlace = bool(this.Flags & 64 != 0)
	return this.hasInterlace, nil
}
func (this *Gif_LocalImageDescriptor) HasSortedColorTable() (v bool, err error) {
	if (this._f_hasSortedColorTable) {
		return this.hasSortedColorTable, nil
	}
	this._f_hasSortedColorTable = true
	this.hasSortedColorTable = bool(this.Flags & 32 != 0)
	return this.hasSortedColorTable, nil
}

/**
 * @see <a href="https://www.w3.org/Graphics/GIF/spec-gif89a.txt">- section 18</a>
 */
type Gif_LogicalScreenDescriptorStruct struct {
	ScreenWidth uint16
	ScreenHeight uint16
	Flags uint8
	BgColorIndex uint8
	PixelAspectRatio uint8
	_io *kaitai.Stream
	_root *Gif
	_parent *Gif
	_f_colorTableSize bool
	colorTableSize int
	_f_hasColorTable bool
	hasColorTable bool
}
func NewGif_LogicalScreenDescriptorStruct() *Gif_LogicalScreenDescriptorStruct {
	return &Gif_LogicalScreenDescriptorStruct{
	}
}

func (this Gif_LogicalScreenDescriptorStruct) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_LogicalScreenDescriptorStruct) Read(io *kaitai.Stream, parent *Gif, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp46, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.ScreenWidth = uint16(tmp46)
	tmp47, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.ScreenHeight = uint16(tmp47)
	tmp48, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Flags = tmp48
	tmp49, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.BgColorIndex = tmp49
	tmp50, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.PixelAspectRatio = tmp50
	return err
}
func (this *Gif_LogicalScreenDescriptorStruct) ColorTableSize() (v int, err error) {
	if (this._f_colorTableSize) {
		return this.colorTableSize, nil
	}
	this._f_colorTableSize = true
	this.colorTableSize = int(2 << (this.Flags & 7))
	return this.colorTableSize, nil
}
func (this *Gif_LogicalScreenDescriptorStruct) HasColorTable() (v bool, err error) {
	if (this._f_hasColorTable) {
		return this.hasColorTable, nil
	}
	this._f_hasColorTable = true
	this.hasColorTable = bool(this.Flags & 128 != 0)
	return this.hasColorTable, nil
}
type Gif_Subblock struct {
	LenBytes uint8
	Bytes []byte
	_io *kaitai.Stream
	_root *Gif
	_parent kaitai.Struct
}
func NewGif_Subblock() *Gif_Subblock {
	return &Gif_Subblock{
	}
}

func (this Gif_Subblock) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_Subblock) Read(io *kaitai.Stream, parent kaitai.Struct, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp51, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.LenBytes = tmp51
	tmp52, err := this._io.ReadBytes(int(this.LenBytes))
	if err != nil {
		return err
	}
	tmp52 = tmp52
	this.Bytes = tmp52
	return err
}
type Gif_Subblocks struct {
	Entries []*Gif_Subblock
	_io *kaitai.Stream
	_root *Gif
	_parent kaitai.Struct
}
func NewGif_Subblocks() *Gif_Subblocks {
	return &Gif_Subblocks{
	}
}

func (this Gif_Subblocks) IO_() *kaitai.Stream {
	return this._io
}

func (this *Gif_Subblocks) Read(io *kaitai.Stream, parent kaitai.Struct, root *Gif) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 1;; i++ {
		tmp53 := NewGif_Subblock()
		err = tmp53.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		_it := tmp53
		this.Entries = append(this.Entries, _it)
		if _it.LenBytes == 0 {
			break
		}
	}
	return err
}