#!/usr/bin/ruby def bin(str) ret = 0 str.length.times { |i| ret += str[i] << (8*i) } ret end class Color attr_reader :r, :g, :b def initialize(fd) @r = bin(fd.read(1)) @g = bin(fd.read(1)) @b = bin(fd.read(1)) end def ==(other) (@r == other.r) and (@g == other.g) and (@b == other.b) end def inspect "<#@r, #@g, #@b>" end alias :to_s :inspect end class GIFBlock attr_reader :type, :subblocks def initialize(fd) typeid = bin(fd.read(1)) @type = case typeid when 0x01 'plain text extension' when 0x2C 'image descriptor??' when 0xF9 'graphic control block' when 0xFE 'comment' when 0xFF 'application extension' else '0x%.2X' % typeid end @blocks = Array.new loop do bsz = bin(fd.read(1)) break if bsz == 0 @blocks << fd.read(bsz) end end end class GIFImage attr_reader :left, :top, :height, :width attr_reader :loc_colortable_flag, :interlaced, :reserved, :loc_colortable_size attr_reader :colortable, :extensions, :lzw_raw_data, :lzw_initsz def initialize(fd) @left = bin(fd.read(2)) @top = bin(fd.read(2)) @height = bin(fd.read(2)) @width = bin(fd.read(2)) tmp = bin(fd.read(1)) @loc_colortable_flag = tmp >> 7 @interlaced = (tmp >> 6) & 1 @sort = (tmp >> 5) & 1 @reserved = (tmp >> 4) & 3 @loc_colortable_size = 2 ** ((tmp & 7)+1) if @loc_colortable_flag == 1 @colortable = Array.new @loc_colortable_size.times { @colortable << Color.new(fd) } end @lzw_initsz = bin(fd.read(1)) # table-based image @lzw_raw_data = '' loop do bsz = bin(fd.read(1)) break if bsz == 0 @lzw_raw_data << fd.read(bsz) end # @pixels = lzw_inflate(@lzw_raw_data, @lzw_initsz) end end class GIF attr_reader :screenwidth, :screenheight, :bgcoloridx, :pixelration attr_reader :glob_colortable_flag, :color_res, :sort_flag, :glob_colortable_size attr_reader :colortable, :images, :blocks, :trailing_bytes alias :palette :colortable def initialize(filename = 'Georges_Painvin_jj.gif') fd = File.open(filename) # signature sig = fd.read 6 raise RuntimeError.new('Not a GIF file') if sig != "GIF89a" # logical screen descriptor @screenwidth = bin(fd.read(2)) @screenheight = bin(fd.read(2)) tmp = bin(fd.read(1)) @glob_colortable_flag = tmp >> 7 @color_res = (tmp >> 4) & 0x7 @sort_flag = (tmp & 0x8) >> 3 @glob_colortable_size = 2 ** ((tmp & 0x7)+1) @bgcoloridx = bin(fd.read(1)) @pixelratio = bin(fd.read(1)) # global color table if @glob_colortable_flag == 1 @colortable = Array.new @glob_colortable_size.times { @colortable << Color.new(fd) } end # blocks @images = Array.new @blocks = Array.new loop do c = bin(fd.read(1)) case c when 0x3b # terminator break when 0x21 # extension @blocks << GIFBlock.new(fd) when 0x2C # image @blocks << GIFImage.new(fd) when nil @blocks << 'eof' break else @blocks << "invalid gif block: id #{c}" end end @trailing_bytes = fd.read fd.close end end if ARGV[0] gif = GIF.new(ARGV[0]) puts gif.inspect end