Module: Flipbook::GIF::LZW

Defined in:
lib/flipbook/gif/lzw.rb,
lib/flipbook/gif/reader.rb

Class Method Summary collapse

Class Method Details

.decode(bytes, minimum_code_size, expected_size) ⇒ Object

Raises:



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/flipbook/gif/reader.rb', line 186

def decode(bytes, minimum_code_size, expected_size)
  clear = 1 << minimum_code_size
  ending = clear + 1
  prefix = Array.new(4096)
  suffix = Array.new(4096)
  clear.times { |index| suffix[index] = index }
  next_code = ending + 1
  width = minimum_code_size + 1
  bit_offset = 0
  previous = nil
  first = nil
  output = String.new(capacity: expected_size, encoding: Encoding::BINARY)
  ended = false

  loop do
    code = read_code(bytes, bit_offset, width)
    break unless code

    bit_offset += width
    if code == clear
      clear.times { |index| suffix[index] = index }
      next_code = ending + 1
      width = minimum_code_size + 1
      previous = nil
      next
    end
    if code == ending
      ended = true
      break
    end

    if code < next_code && suffix[code]
      entry, first = expand(code, clear, prefix, suffix)
    elsif code == next_code && previous
      entry, first = expand(previous, clear, prefix, suffix)
      entry << first
    else
      raise Error, "invalid GIF LZW code"
    end
    output << entry.pack("C*")
    raise Error, "GIF frame data exceeds its dimensions" if output.bytesize > expected_size

    if previous && next_code < 4096
      prefix[next_code] = previous
      suffix[next_code] = first
      next_code += 1
      width += 1 if next_code == (1 << width) && width < 12
    end
    previous = code
  end
  raise Error, "truncated GIF LZW data" unless ended && output.bytesize == expected_size

  output.bytes
end

.encode(indices, minimum_code_size) ⇒ Object

Raises:

  • (ArgumentError)


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/flipbook/gif/lzw.rb', line 8

def encode(indices, minimum_code_size)
  minimum_code_size = Integer(minimum_code_size)
  raise ArgumentError, "minimum code size must be between 2 and 8" unless minimum_code_size.between?(2, 8)

  clear_code = 1 << minimum_code_size
  end_code = clear_code + 1
  next_code = end_code + 1
  code_width = minimum_code_size + 1
  dictionary = {}
  writer = BitWriter.new
  writer.write(clear_code, code_width)
  bytes = String(indices).b
  return writer.tap { |bits| bits.write(end_code, code_width) }.sub_blocks if bytes.empty?

  prefix = bytes.getbyte(0)
  bytes.byteslice(1..).to_s.each_byte do |byte|
    key = (prefix << 8) | byte
    if dictionary.key?(key)
      prefix = dictionary[key]
      next
    end

    writer.write(prefix, code_width)
    if next_code < 4096
      dictionary[key] = next_code
      next_code += 1
      code_width += 1 if next_code > (1 << code_width) && code_width < 12
    else
      writer.write(clear_code, code_width)
      dictionary.clear
      next_code = end_code + 1
      code_width = minimum_code_size + 1
    end
    prefix = byte
  end
  writer.write(prefix, code_width)
  code_width += 1 if next_code == (1 << code_width) && code_width < 12
  writer.write(end_code, code_width)
  writer.sub_blocks
end