=begin = gifcat.rb gifcat.rb is a library for concatenation of gif images. == version v0.0.1 2002/09/18 initial version. == License Copyright (c) 2002 TAKAHASHI 'Maki' Masayoshi (Original version) Copyright (c) 1997,2002 http://tohoho.wakusei.ne.jp/ This library is free software; you can use, copy, modify and re-distribute it with above copyright notices and this permission notice. == Original Header ;# ==================================================================== ;# ;# gifcat.pl: GIFファイル連結ライブラリ Ver1.61 ;# ;# Copyright (c) 1997,2002 http://tohoho.wakusei.ne.jp/ ;# ;# 著作権は放棄しませんが、自由に使用・改造・再配布可能です。 ;# ;# 基本的な使い方 (略) ;# 制限事項 ;# アニメGIF同士を連結することはできません。 ;# アニメGIF対応のブラウザでなければ、最初の画像しか表示されません。 ;# 高さの異なるGIFファイルは連結できません。 ;# ;# 最新版入手先 ;# http://tohoho.wakusei.ne.jp/wwwsoft.htm ;# ;# 更新履歴: (略) ;# ;# ==================================================================== =end class GIFCat attr_accessor :debug def initialize() @debug = nil end ## # get a concatenated GIF image. # def read_files(*files) @data = [] @gif = 0 @logicalScreenWidth = 0 @logicalScreenHeight = 0 @useLocalColorTable = 0 @packedFields18 = [] @packedFields20 = [] @packedFields23 = [] @globalColorTable= [] @transparentColorFlag = [] @transparentColorIndex = [] @imageWidth = [] @imageHeight = 0 @lzwMinimumCodeSize = [] @imageData = [] files.each{|file| begin File.open(file,"rb"){|f| @buf = f.read() } size = @buf.size rescue raise "ERROR" end @cnt = 0 header() loop do x1 = @buf[@cnt] if x1 == 0x2c image_block() elsif x1 == 0x21 x2 = @buf[@cnt+1] case x2 when 0xf9 graphic_control_extension() when 0xfe comment_extension() when 0x01 plaintext_extension() when 0xff application_extension(); else raise "ERROR" end elsif x1 == 0x3b trailer() break elsif @cnt == size break else raise "ERROR" end end @buf = nil @gif += 1 } end def to_gif if @gif < 1 raise "no data" end gifImage = "GIF89a" # signature and version gifImage += [@logicalScreenWidth & 0x00ff, (@logicalScreenWidth & 0xff00) >> 8, @logicalScreenHeight & 0x00ff, (@logicalScreenHeight & 0xff00) >> 8].pack("CCCC") if @useLocalColorTable != 0 @packedFields18[0] &= ~0x80 end gifImage += [@packedFields18[0]].pack("C") gifImage += [@backgroundColorIndex].pack("C") gifImage += [@pixelAspectRatio].pack("C") if @useLocalColorTable == 0 gifImage += @globalColorTable[0] end # i = -1 i = 0 leftpos = 0; while (i < @gif) # j = (i == -1) ? 0 : i j = i gifImage += [0x21, 0xf9, 0x04].pack("CCC") gifImage += [@packedFields23 | @transparentColorFlag[j]].pack("C") gifImage += [0x00, 0x00].pack("CC"); gifImage += [@transparentColorIndex[j]].pack("C") gifImage += [0x00].pack("C") gifImage += [0x2c].pack("C") # image block n = leftpos # leftpos += (i == -1) ? 0 : @imageWidth[j] leftpos += @imageWidth[j] gifImage += [n & 0x00ff].pack("C"); gifImage += [(n & 0xff00) >> 8].pack("C"); gifImage += [0x00, 0x00].pack("CC"); gifImage += [@imageWidth[j] & 0x00ff].pack("C") gifImage += [(@imageWidth[j] & 0xff00) >> 8].pack("C") gifImage += [@imageHeight & 0x00ff].pack("C") gifImage += [(@imageHeight & 0xff00) >> 8].pack("C") if @useLocalColorTable != 0 @packedFields20[j] |= 0x80; @packedFields20[j] &= ~0x07; @packedFields20[j] |= (@packedFields18[j] & 0x07) gifImage += [@packedFields20[j]].pack("C") gifImage += @globalColorTable[j] else gifImage += [@packedFields20[j]].pack("C") end gifImage += [@lzwMinimumCodeSize[j]].pack("C") gifImage += @imageData[j] i += 1 end gifImage += [0x3b].pack("C") return gifImage end ## # GifHeader # def header() signature = @buf[@cnt, 3] version = @buf[@cnt+3, 3] logicalScreenWidth = @buf[@cnt+6] + @buf[@cnt+7]*256 logicalScreenHeight = @buf[@cnt+8] + @buf[@cnt+9]*256 @packedFields18[@gif] = @buf[@cnt+10] globalColorTableFlag = (@packedFields18[@gif] & 0x80) >> 7 colorResolution = ((@packedFields18[@gif] & 0x70) >> 4) + 1 sortFlag = (@packedFields18[@gif] & 0x08) >> 3 sizeOfGlobalColorTable = 2 ** ((@packedFields18[@gif] & 0x07) + 1) @backgroundColorIndex = @buf[@cnt+11] @pixelAspectRatio = @buf[@cnt+12] @cnt += 13 if globalColorTableFlag != 0 globalColorTable = @buf[@cnt, sizeOfGlobalColorTable * 3] @cnt += sizeOfGlobalColorTable * 3 else globalColorTable = "" end @logicalScreenWidth += logicalScreenWidth if @logicalScreenHeight < logicalScreenHeight @logicalScreenHeight = logicalScreenHeight end if globalColorTableFlag != 0 @globalColorTable[@gif] = globalColorTable if @gif > 0 if globalColorTable != @globalColorTable[@gif - 1] @useLocalColorTable = 1 end end end if @debug printf "=====================================\n" printf "GifHeader\n" printf "=====================================\n" printf "Signature: %s\n", signature printf "Version: %s\n", version printf "Logical Screen Width: %d\n", logicalScreenWidth printf "Logical Screen Height: %d\n", logicalScreenHeight printf "Global Color Table Flag: %d\n", globalColorTableFlag printf "Color Resolution: %d\n", colorResolution printf "Sort Flag: %d\n", sortFlag printf "Size of Global Color Table: %d * 3\n", sizeOfGlobalColorTable printf "Background Color Index: %d\n", @backgroundColorIndex printf "Pixel Aspect Ratio: %d\n", @pixelAspectRatio printf "Global Color Table: \n" dump(globalColorTable) end end ## # Image Block # def image_block imageSeparator = @buf[@cnt] imageLeftPosition = @buf[@cnt+1] + @buf[@cnt+2]*256 imageTopPosition = @buf[@cnt+3] + @buf[@cnt+4]*256 @imageWidth[@gif] = @buf[@cnt+5] + @buf[@cnt+6]*256 @imageHeight = @buf[@cnt+7] + @buf[@cnt+8]*256 @packedFields20[@gif] = @buf[@cnt+9] @cnt += 10 localColorTableFlag = (@packedFields20[@gif] & 0x80) >> 7 interlaceFlag = (@packedFields20[@gif] & 0x40) >> 6 sortFlag = (@packedFields20[@gif] & 0x20) >> 5 reserved = (@packedFields20[@gif] & 0x18) >> 3 if localColorTableFlag != 0 sizeOfLocalColorTable = 2 ** ((@packedFields20[@gif] & 0x07) + 1) localColorTable = @buf[@cnt, sizeOfLocalColorTable * 3] @cnt += sizeOfLocalColorTable * 3 else sizeOfLocalColorTable = 0 localColorTable = "" end @lzwMinimumCodeSize[@gif] = @buf[@cnt] @cnt += 1 @imageData[@gif] = dataSubBlock() if @debug printf "=====================================\n" printf "Image Block\n" printf "=====================================\n" printf "Image Separator: 0x%02x\n", imageSeparator printf "Image Left Position: %d\n", imageLeftPosition printf "Image Top Position: %d\n", imageTopPosition printf "Image Width: %d\n", @imageWidth[@gif] printf "Image Height: %d\n", @imageHeight printf "Local Color Table Flag: %d\n", localColorTableFlag printf "Interlace Flag: %d\n", interlaceFlag printf "Sort Flag: %d\n", sortFlag printf "Reserved: --\n" printf "Size of Local Color Table: %d\n", sizeOfLocalColorTable printf "Local Color Table: \n" dump(localColorTable) printf "LZW Minimum Code Size: %d\n", @lzwMinimumCodeSize[@gif] printf "Image Data: \n" dump(@imageData[@gif]) printf "Block Terminator: 0x00\n" end end ## # Graphic Control Extension # def graphic_control_extension extensionIntroducer = @buf[@cnt] graphicControlLabel = @buf[@cnt+1] blockSize = @buf[@cnt+2] @packedFields23 = @buf[@cnt+3] reserved = (@packedFields23 & 0xe0) >> 5 disposalMethod = (@packedFields23 & 0x1c) >> 5 userInputFlag = (@packedFields23 & 0x02) >> 1 @transparentColorFlag[@gif] = @packedFields23 & 0x01 delayTime = @buf[@cnt+4] + @buf[@cnt+5]*256 @transparentColorIndex[@gif] = @buf[@cnt+6] blockTerminator = @buf[@cnt+7] @cnt += 8 if @debug printf "=====================================\n" printf "Graphic Control Extension\n" printf "=====================================\n" printf "Extension Introducer: 0x%02x\n", extensionIntroducer printf "Graphic Control Label: 0x%02x\n", graphicControlLabel printf "Block Size: %d\n", blockSize printf "Reserved: --\n" printf "Disposal Method: %d\n", disposalMethod printf "User Input Flag: %d\n", userInputFlag printf "Transparent Color Flag: %d\n", @transparentColorFlag[@gif] printf "Delay Time: %d\n", delayTime printf "Transparent Color Index: %d\n", @transparentColorIndex[@gif] printf "Block Terminator: 0x%02x\n", blockTerminator end end ## # Comment Extension # def comment_extension extensionIntroducer = @buf[@cnt] commentLabel = @buf[@cnt+1] @cnt += 2 comment = dataSubBlock() if @debug printf "=====================================\n" printf "Comment Extension\n" printf "=====================================\n" printf "Extension Introducer: 0x%02x\n", extensionIntroducer printf "Comment Label: 0x%02x\n", commentLabel printf "Comment Data: \n" dump(comment) printf "Block Terminator: 0x00\n" end end ## # Plain Text Extension # def plaintext_extension extensionIntroducer = @buf[@cnt] plainTextLabel = @buf[@cnt+1] blockSize = @buf[@cnt+2] textGridLeftPosition = @buf[@cnt+3] + @buf[@cnt+4]*256 textGridTopPosition = @buf[@cnt+5] + @buf[@cnt+6]*256 textGridWidth = @buf[@cnt+7] + @buf[@cnt+8]*256 textGridHeight = @buf[@cnt+8] + @buf[@cnt+10]*256 characterCellWidth = @buf[@cnt+11] characterCellHeight = @buf[@cnt+12] textForegroundColorIndex = @buf[@cnt+13] textBackgroundColorIndex = @buf[@cnt+14] @cnt += 15 dataSubBlock() if @debug printf "=====================================\n" printf "Plain Text Extension\n" printf "=====================================\n" printf "Extension Introducer: 0x%02x\n", extensionIntroducer printf "Plain Text Label: 0x%02x\n", plainTextLabel printf "Block Size: 0x%02x\n", blockSize printf "Text Grid Left Position: %d\n", textGridLeftPosition printf "Text Grid Top Position: %d\n", textGridTopPosition printf "Text Grid Width: %d\n", textGridWidth printf "Text Grid Height: %d\n", textGridHeight printf "Text Foreground Color Index: %d\n", textForegroundColorIndex printf "Text Background Color Index: %d\n", textBackgroundColorIndex printf "Plain Text Data: ...\n" printf "Block Terminator: 0x00\n" end end ## # Application Extension # def application_extension extensionIntroducer = @buf[@cnt] extentionLabel = @buf[@cnt+1] blockSize = @buf[@cnt+2] applicationIdentifire = @buf[@cnt+3, 8] applicationAuthenticationCode = @buf[@cnt+11, 3] @cnt += 14 dataSubBlock() if @debug printf "=====================================\n" printf "Application Extension\n" printf "=====================================\n" printf "Extension Introducer: 0x%02x\n", extensionIntroducer printf "Extension Label: 0x%02x\n", plainTextLabel printf "Block Size: 0x%02x\n", blockSize printf "Application Identifire: ...\n" printf "ApplicationAuthenticationCode: ...\n" printf "Block Terminator: 0x00\n" end end ## # Trailer # def trailer trailer = @buf[@cnt] @cnt += 1 if @debug printf "=====================================\n" printf "Trailer\n" printf "=====================================\n" printf "Trailer: 0x%02x\n", trailer printf "\n" end end ## # Data Sub Block # def dataSubBlock() from = @cnt while ((n = @buf[@cnt]) != 0 && n != nil) @cnt += 1 @cnt += n end if !n.nil? @cnt += 1 end return @buf[from, @cnt - from] end ## # Memory Dump # def dump(buf) if buf.size == 0 return end buf.split(//n).each_with_index{|chr, i| if i % 16 == 0 print " " end printf "%02X ", chr[0] if i % 16 == 15 printf "\n" end } if buf.size % 16 != 0 printf "\n" end end end if __FILE__ == $0 ## short cut: # print GIFCat.new.read_files("foo.gif", "bar.gif") ## gifファイルの情報出力 #gif = GIFCat.new #gif.debug = true #gif.read_files("tmp1.gif", "tmp2.gif", "tmp3.gif") ### gifファイル連結 gif = GIFCat.new gif.read_files("tmp1.gif", "tmp2.gif", "tmp3.gif") print gif.to_gif end