Class: Gifenc::Gif

Inherits:
Object
  • Object
show all
Defined in:
lib/gif.rb

Overview

Represents a GIF file, possibly composed of multiple images. Note that each image in a GIF file is not necessarily an animation frame, they could also be still images that should be layered on top of each other.

Constant Summary collapse

HEADER =

6-byte block indicating the beginning of the GIF data stream. It is composed of the signature (GIF) and the version (89a).

'GIF89a'
TRAILER =

1-byte block indicating the termination of the GIF data stream.

';'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width, height, gct: nil, color: DEFAULT_COLOR, interlace: DEFAULT_INTERLACE, delay: nil, trans_color: nil, disposal: nil, loops: DEFAULT_LOOPS, bg: DEFAULT_BACKGROUND, ar: DEFAULT_ASPECT_RATIO, destroy: false) ⇒ Gif

Creates a new GIF object.

Parameters:

  • width (Integer)

    Width of the logical screen (canvas) in pixels (see #width and #resize).

  • height (Integer)

    Height of the logical screen (canvas) in pixels (see #height and #resize).

  • gct (ColorTable) (defaults to: nil)

    The global color table of the GIF (see #gct).

  • color (Integer) (defaults to: DEFAULT_COLOR)

    Default frame color (see #color).

  • delay (Integer) (defaults to: nil)

    Default delay between frames (see #delay).

  • trans_color (Integer) (defaults to: nil)

    Default transparent color (see #trans_color).

  • disposal (Integer) (defaults to: nil)

    Default disposal method (see #disposal).

  • loops (Integer) (defaults to: DEFAULT_LOOPS)

    Amount of times to loop the GIF (see #loops).

  • bg (Integer) (defaults to: DEFAULT_BACKGROUND)

    Background color (see #bg).

  • ar (Integer) (defaults to: DEFAULT_ASPECT_RATIO)

    Pixel aspect ratio (see #ar).

  • destroy (Boolean) (defaults to: false)

    Auto-destroy each image right after encoding (see #destroy).



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/gif.rb', line 118

def initialize(
    width,
    height,
    gct:         nil,
    color:       DEFAULT_COLOR,
    interlace:   DEFAULT_INTERLACE,
    delay:       nil,
    trans_color: nil,
    disposal:    nil,
    loops:       DEFAULT_LOOPS,
    bg:          DEFAULT_BACKGROUND,
    ar:          DEFAULT_ASPECT_RATIO,
    destroy:     false
  )
  # GIF attributes
  @width  = width
  @height = height
  @bg     = bg
  @ar     = ar
  @gct    = gct

  # Default image attributes
  @color       = color
  @interlace   = interlace
  @delay       = delay
  @trans_color = trans_color
  @disposal    = disposal

  # GIF content data
  @images     = []
  @extensions = []

  # Other
  @destroy = destroy

  # If we want the GIF to loop, then add the Netscape Extension
  self.loops = loops
end

Instance Attribute Details

#arInteger

Note:

This field is ignored by most decoders, which instead render all pixels square.

Aspect ratio of the pixels. If provided (ar > 0), the aspect ratio is calculated as (ar + 15) / 64, which allows for ratios roughly between 1:4 and 4:1 in increments of 1/64th. 0 means square pixels.

Returns:

  • (Integer)

    Pixel aspect ratio.



84
85
86
# File 'lib/gif.rb', line 84

def ar
  @ar
end

#bgInteger

Note:

This field is ignored by most decoders, which instead render the exposed parts of the canvas transparently.

Index of the background color in the Global Color Table. This is the color of the exposed parts of the canvas, i.e., those not covered by any image.

Returns:

  • (Integer)

    Index of the background color.



76
77
78
# File 'lib/gif.rb', line 76

def bg
  @bg
end

#colorInteger

The default color of the GIF's images. This color is used to initialize new blank images, as well as to pad images when they are resized to a bigger size. This can be overriden individually for each image.

Returns:

  • (Integer)

    Index of the default image color.



42
43
44
# File 'lib/gif.rb', line 42

def color
  @color
end

#delayInteger

The default delay to use between images, in 1/100ths of a second. See Extension::GraphicControl#delay for details about its implementation. This can be overriden individually for each image.

Returns:

  • (Integer)

    Delay beween frames.



48
49
50
# File 'lib/gif.rb', line 48

def delay
  @delay
end

#destroyBoolean

Note:

This will make the images unusable after the first encoding! So if you plan on reusing them multiple times, do not enable this setting, or only do so before the very last encoding.

Automatically destroy each image right after processing it during encoding. This is intended to save as much memory as possible whenever that's sensitive. Normally, since the pixel data for each image gets encoded and appended to the final output, the data is briefly duplicated in memory. This avoids it.

Returns:

  • (Boolean)

    Auto-destroy mode.



104
105
106
# File 'lib/gif.rb', line 104

def destroy
  @destroy
end

#disposalInteger

The default disposal method to use for every image in the GIF. The disposal method handles how each frame is disposed of before displaying the next one. See Extension::GraphicControl#disposal for the specific details. This can be overriden individually for each image.

Returns:

  • (Integer)

    Default frame disposal method.



61
62
63
# File 'lib/gif.rb', line 61

def disposal
  @disposal
end

#extensionsArray<Extension>

The array of global extensions present in the GIF. This may include Application Extensions, Comment Extensions, etc. Other extensions, like the Graphic Control Extension, are local to each image, and are set there.

Returns:



94
95
96
# File 'lib/gif.rb', line 94

def extensions
  @extensions
end

#gctColorTable

Note:

Changing this table after images have already been created with it will NOT update their color indices, which will thus corrupt them in the final encoded GIF.

The Global Color Table. This represents the default palette of all the images in the GIF. In other words, all colors in all images are indexed in this table, unless a local table is explicitly provided for an image, in which case it overrides the global one. A color table may contain up to 256 colors. See ColorTable for more details and a list of default palettes.

Returns:



36
37
38
# File 'lib/gif.rb', line 36

def gct
  @gct
end

#heightInteger (readonly)

The height of the GIF's logical screen, i.e., its canvas. To resize it, use the #resize method.

Returns:

  • (Integer)

    Width of the logical screen in pixels.

See Also:



25
26
27
# File 'lib/gif.rb', line 25

def height
  @height
end

#imagesArray<Image>

The array of images present in the GIF.

Returns:

  • (Array<Image>)

    Image list.



88
89
90
# File 'lib/gif.rb', line 88

def images
  @images
end

#loopsInteger

The amount of times to loop the GIF. Must be a number between -1 and 65535, where -1 means to loop indefinitely. Internally, any non-zero number will result in the creation of a Netscape Extension. Note that many programs do not support finite loop counts, instead rendering all GIFs as either fully static or looping indefinitely.

Returns:

  • (Integer)

    GIF loop count.



69
70
71
# File 'lib/gif.rb', line 69

def loops
  @loops
end

#trans_colorInteger

The index of the default color to use as transparent. For details about how transparency works in GIF files, see Extension::GraphicControl#trans_color. This can be overriden individually for each image.

Returns:

  • (Integer)

    Index of the default transparent color.



54
55
56
# File 'lib/gif.rb', line 54

def trans_color
  @trans_color
end

#widthInteger (readonly)

The width of the GIF's logical screen, i.e., its canvas. To resize it, use the #resize method.

Returns:

  • (Integer)

    Width of the logical screen in pixels.

See Also:



19
20
21
# File 'lib/gif.rb', line 19

def width
  @width
end

Instance Method Details

#boomerang(first: false) ⇒ Gif

Duplicates all frames in reverse order. Generates a boomerang effect, particularly neat when looped.

Parameters:

  • first (Boolean) (defaults to: false)

    Whether to duplicate the very first frame. Probably not desired for looping GIFs, because then the first frame would appear twice in a row (at the start, and at the end). Useful for non-looping boomerangs.

Returns:

  • (Gif)

    The GIF object.



239
240
241
242
243
244
# File 'lib/gif.rb', line 239

def boomerang(first: false)
  (@images.size - 2).downto(first ? 0 : 1).each{ |i|
    @images << @images[i].dup
  }
  self
end

#cycleGif

Shortcut to loop the GIF indefinitely. This sets the loops count to -1, which translates to 0 in the Netscape Extension.

Returns:

  • (Gif)

    The GIF object.



227
228
229
230
# File 'lib/gif.rb', line 227

def cycle
  self.loops = -1
  self
end

#encode(stream) ⇒ Object

Encode all the data as a GIF file and write it to a stream.

Parameters:

  • stream (IO)

    Stream to write the data to.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/gif.rb', line 159

def encode(stream)
  # Header
  stream << HEADER

  # Logical Screen Descriptor
  stream << [@width, @height].pack('S<2')
  flags = 0
  flags |= @gct.global_flags if @gct
  stream << [flags].pack('C')
  stream << [@bg, @ar].pack('C2')

  # Global Color Table
  @gct.encode(stream) if @gct

  # Global extensions
  @extensions.each{ |e| e.encode(stream) }

  # Encode frames containing image data (and local extensions)
  @images.size.times.each{ |i|
    @images[i].encode(stream)
    if @destroy
      @images[i].destroy
      @images[i] = nil
    end
  }

  # Trailer
  stream << TRAILER
end

#exhibit(delay = DEFAULT_EXHIBIT_TIME) ⇒ Gif

Shortcut to modify the delay of the GIF's last frame, intended to showcase the final result before looping it.

Parameters:

  • delay (Integer) (defaults to: DEFAULT_EXHIBIT_TIME)

    The time, in 1/100ths of a second, to show the last frame.

Returns:

  • (Gif)

    The GIF object.



251
252
253
254
# File 'lib/gif.rb', line 251

def exhibit(delay = DEFAULT_EXHIBIT_TIME)
  @images.last.delay = delay
  self
end

#resize(width, height) ⇒ Object

TODO:

We should probably test what happens when images are out of the logical screen's bounds, and perform integrity checks here if necessary, perhaps cropping the images present in this GIF.

Change the dimensions of the GIF's logical screen, i.e, its canvas.



193
194
195
196
# File 'lib/gif.rb', line 193

def resize(width, height)
  @width = width
  @height = height
end

#save(filename) ⇒ Object

Encode and write the GIF to a file.

Parameters:

  • filename (String)

    Name of the output file.



267
268
269
270
271
# File 'lib/gif.rb', line 267

def save(filename)
  File.open(filename, 'wb') do |f|
    encode(f)
  end
end

#stillGif

Shortcut to not loop the GIF at all. This sets the loop count to 0 and removes Netscape Extension. The name of the method comes from the fact that this is typically done to make a still image instead of an animation, but if multiple frames are added, they will be played back once. Crucially, if we want to make a layered image (i.e., a still image with multiple tiles and no delay between them, a common trick to achieve more than 256 colors in a GIF image), it's usually mandatory to do this, as most decoders will actually assume multiple images in a GIF are animation frames, rather than layers of a still image, if the Netscape Extension is present.

Returns:

  • (Gif)

    The GIF object.



219
220
221
222
# File 'lib/gif.rb', line 219

def still
  self.loops = 0
  self
end

#writeString

Encode and write the GIF to a string.

Returns:

  • (String)

    The string containing the encoded GIF file.



258
259
260
261
262
263
# File 'lib/gif.rb', line 258

def write
  str = StringIO.new
  str.set_encoding("ASCII-8BIT")
  encode(str)
  str.string
end