GIFの仕様書を見ながら小さなGIFファイルを読んでみました。調査結果を基に手を加えてアニメーション化を試み、差分を示しました。
シリーズの記事です。
- GIFのバイナリを読んでみた ← この記事
- GIFのLZW圧縮を調べてみた
- GIFのLZWを展開してみた
参考
英語版Wikipediaと、そこからリンクされている仕様書を参照しました。
サンプル
GIMPで16×16の画像を作りました。
バイナリダンプ
00000000 47 49 46 38 37 61 10 00 10 00 a1 02 00 00 00 00 |GIF87a..........|
00000010 80 80 80 ff ff ff ff ff ff 2c 00 00 00 00 10 00 |.........,......|
00000020 10 00 00 02 2b 94 8f a9 16 b0 0b 42 78 c8 09 4b |....+......Bx..K|
00000030 b1 a4 fb 60 f5 5d 4f 28 74 47 03 a4 91 07 22 92 |...`.]O(tG....".|
00000040 14 bd e5 b4 98 62 56 51 f7 ac db fa fe 43 14 00 |.....bVQ.....C..|
00000050 00 3b |.;|
解読
仕様書を見ながらコメントを付けます。仕様書の該当するセクションを記載します。
- F#で文字の
B
や数値のuy
はbyte
型を表すサフィックスで、省略できません。 - Width, Height などの2バイト値はリトルエンディアンです。
GIFTest.fsx
let gif = [|
// 17. Header.
'G'B; 'I'B; 'F'B // Signature
'8'B; '7'B; 'a'B // Version
// 18. Logical Screen Descriptor.
16uy; 0uy // Logical Screen Width
16uy; 0uy // Logical Screen Height
0xA1uy // <Packed Fields>
// Global Color Table Flag
// | Color Resolution
// | | Sort Flag
// | | | Size of Global Color Table
// 1 010 0 001
2uy // Background Color Index
0uy // Pixel Aspect Ratio
// 19. Global Color Table.
0x00uy; 0x00uy; 0x00uy // RGB #0
0x80uy; 0x80uy; 0x80uy // RGB #1
0xFFuy; 0xFFuy; 0xFFuy // RGB #2
0xFFuy; 0xFFuy; 0xFFuy // RGB #3
// 20. Image Descriptor.
0x2Cuy // Image Separator
0uy ; 0uy // Image Left Position
0uy ; 0uy // Image Top Position
16uy; 0uy // Image Width
16uy; 0uy // Image Height
0uy // <Packed Fields>
// Local Color Table Flag
// | Interlace Flag
// | | Sort Flag
// | | | Reserved
// | | | | Size of Local Color Table
// 0 0 0 00 000
// 22. Table Based Image Data.
2uy // LZW Minimum Code Size
// F. Variable-Length-Code LZW Compression.
0x2Buy // block size
// data bytes
0x94uy; 0x8Fuy; 0xA9uy; 0x16uy; 0xB0uy; 0x0Buy; 0x42uy; 0x78uy
0xC8uy; 0x09uy; 0x4Buy; 0xB1uy; 0xA4uy; 0xFBuy; 0x60uy; 0xF5uy
0x5Duy; 0x4Fuy; 0x28uy; 0x74uy; 0x47uy; 0x03uy; 0xA4uy; 0x91uy
0x07uy; 0x22uy; 0x92uy; 0x14uy; 0xBDuy; 0xE5uy; 0xB4uy; 0x98uy
0x62uy; 0x56uy; 0x51uy; 0xF7uy; 0xACuy; 0xDBuy; 0xFAuy; 0xFEuy
0x43uy; 0x14uy; 0x00uy
0uy // Block Terminator
// 27. Trailer.
0x3Buy // GIF Trailer
|]
表示
先ほどのコードに付け足しで画面表示します。
GIFTest.fsx(続き)
#r "System"
#r "System.Drawing"
#r "System.Windows.Forms"
open System
open System.IO
open System.Drawing
open System.Windows.Forms
[<EntryPoint; STAThread>] do
let w = new Form(Text = "GIF", Size = Size(180, 100))
let p = new PictureBox(Dock = DockStyle.Fill,
SizeMode = PictureBoxSizeMode.CenterImage)
try
p.Image <- Image.FromStream(new MemoryStream(gif))
with _ -> ()
w.Controls.Add p
Application.EnableVisualStyles()
Application.Run w
GIFアニメーション化
3ステップでGIFを手動編集してアニメーション化を試みます。
カラーテーブルのローカル化
後で複数のGIFを連結することを想定して、手動でカラーテーブルをローカルに移します。
- Background Color Index: 情報が消えますが、とりあえず無視します。
- Color Resolution: 個々の画像を見ないと分かりませんが、簡単のため111に決め打ちします。
GIFTest-L.diff
--- GIFTest.fsx
+++ GIFTest-L.fsx
@@ -6,34 +6,34 @@
// 18. Logical Screen Descriptor.
16uy; 0uy // Logical Screen Width
16uy; 0uy // Logical Screen Height
- 0xA1uy // <Packed Fields>
+ 0x70uy // <Packed Fields>
// Global Color Table Flag
// | Color Resolution
// | | Sort Flag
// | | | Size of Global Color Table
- // 1 010 0 001
- 2uy // Background Color Index
+ // 0 111 0 000
+ 0uy // Background Color Index
0uy // Pixel Aspect Ratio
- // 19. Global Color Table.
- 0x00uy; 0x00uy; 0x00uy // RGB #0
- 0x80uy; 0x80uy; 0x80uy // RGB #1
- 0xFFuy; 0xFFuy; 0xFFuy // RGB #2
- 0xFFuy; 0xFFuy; 0xFFuy // RGB #3
-
// 20. Image Descriptor.
0x2Cuy // Image Separator
0uy ; 0uy // Image Left Position
0uy ; 0uy // Image Top Position
16uy; 0uy // Image Width
16uy; 0uy // Image Height
- 0uy // <Packed Fields>
+ 0x81uy // <Packed Fields>
// Local Color Table Flag
// | Interlace Flag
// | | Sort Flag
// | | | Reserved
// | | | | Size of Local Color Table
- // 0 0 0 00 000
+ // 1 0 0 00 001
+
+ // 21. Local Color Table.
+ 0x00uy; 0x00uy; 0x00uy // RGB #0
+ 0x80uy; 0x80uy; 0x80uy // RGB #1
+ 0xFFuy; 0xFFuy; 0xFFuy // RGB #2
+ 0xFFuy; 0xFFuy; 0xFFuy // RGB #3
// 22. Table Based Image Data.
2uy // LZW Minimum Code Size
アニメーション化(1フレーム)
ループ回数(0で無限)とフレーム情報を入れてアニメーション化します。1フレームを無限に繰り返すためアニメーションしているようには見えません。
※ ループ回数はNetscapeのアプリケーション拡張として定義します。
GIFTest-A.diff
--- GIFTest-L.fsx
+++ GIFTest-A.fsx
@@ -1,7 +1,7 @@
let gif = [|
// 17. Header.
'G'B; 'I'B; 'F'B // Signature
- '8'B; '7'B; 'a'B // Version
+ '8'B; '9'B; 'a'B // Version
// 18. Logical Screen Descriptor.
16uy; 0uy // Logical Screen Width
@@ -15,6 +15,32 @@
0uy // Background Color Index
0uy // Pixel Aspect Ratio
+ // 26. Application Extension.
+ 0x21uy // Extension Introducer
+ 0xFFuy // Extension Label
+ 11uy // Block Size
+ // Application Identifier
+ 'N'B; 'E'B; 'T'B; 'S'B; 'C'B; 'A'B; 'P'B; 'E'B
+ '2'B; '.'B; '0'B // Appl. Authentication Code
+ 3uy // Application Data Size
+ 1uy // data sub-block index (always 1)
+ 0uy; 0uy // unsigned number of repetitions
+ 0uy // Block Terminator
+
+ // 23. Graphic Control Extension.
+ 0x21uy // Extension Introducer
+ 0xF9uy // Graphic Control Label
+ 4uy // Block Size
+ 0uy // <Packed Fields>
+ // Reserved
+ // | Disposal Method
+ // | | User Input Flag
+ // | | | Transparent Color Flag
+ // 000 000 0 0
+ 10uy; 0uy // Delay Time (10ms * 10 = 0.1s)
+ 0uy // Transparent Color Index
+ 0uy // Block Terminator
+
// 20. Image Descriptor.
0x2Cuy // Image Separator
0x00uy; 0x00uy // Image Left Position
アニメーション化(2フレーム)
パレットを変えて赤字にした同じ絵を追加してアニメーションさせます。
GIFTest-A2.diff
--- GIFTest-A.fsx
+++ GIFTest-A2.fsx
@@ -74,6 +74,53 @@
0x43uy; 0x14uy; 0x00uy
0uy // Block Terminator
+ // 23. Graphic Control Extension.
+ 0x21uy // Extension Introducer
+ 0xF9uy // Graphic Control Label
+ 4uy // Block Size
+ 0uy // <Packed Fields>
+ // Reserved
+ // | Disposal Method
+ // | | User Input Flag
+ // | | | Transparent Color Flag
+ // 000 000 0 0
+ 10uy; 0uy // Delay Time (10ms * 10 = 0.1s)
+ 0uy // Transparent Color Index
+ 0uy // Block Terminator
+
+ // 20. Image Descriptor.
+ 0x2Cuy // Image Separator
+ 0uy ; 0uy // Image Left Position
+ 0uy ; 0uy // Image Top Position
+ 16uy; 0uy // Image Width
+ 16uy; 0uy // Image Height
+ 0x81uy // <Packed Fields>
+ // Local Color Table Flag
+ // | Interlace Flag
+ // | | Sort Flag
+ // | | | Reserved
+ // | | | | Size of Local Color Table
+ // 1 0 0 00 001
+
+ // 21. Local Color Table.
+ 0xFFuy; 0x00uy; 0x00uy // RGB #0
+ 0xFFuy; 0x80uy; 0x80uy // RGB #1
+ 0xFFuy; 0xFFuy; 0xFFuy // RGB #2
+ 0xFFuy; 0xFFuy; 0xFFuy // RGB #3
+
+ // 22. Table Based Image Data.
+ 2uy // LZW Minimum Code Size
+ // F. Variable-Length-Code LZW Compression.
+ 0x2Buy // block size
+ // data bytes
+ 0x94uy; 0x8Fuy; 0xA9uy; 0x16uy; 0xB0uy; 0x0Buy; 0x42uy; 0x78uy
+ 0xC8uy; 0x09uy; 0x4Buy; 0xB1uy; 0xA4uy; 0xFBuy; 0x60uy; 0xF5uy
+ 0x5Duy; 0x4Fuy; 0x28uy; 0x74uy; 0x47uy; 0x03uy; 0xA4uy; 0x91uy
+ 0x07uy; 0x22uy; 0x92uy; 0x14uy; 0xBDuy; 0xE5uy; 0xB4uy; 0x98uy
+ 0x62uy; 0x56uy; 0x51uy; 0xF7uy; 0xACuy; 0xDBuy; 0xFAuy; 0xFEuy
+ 0x43uy; 0x14uy; 0x00uy
+ 0uy // Block Terminator
+
// 27. Trailer.
0x3Buy // GIF Trailer
|]
感想
GIFはシンプルな構造で、バイナリいじりの入門にも良いかもしれないと思いました。結果がアニメーションするので面白いです。