デバッグ用のツール
GIF ファイルのデータ形式は難しいものではないので、ライブラリ等を用いなくても作る事が比較的容易です。すると、作った GIF ファイルの状態を確認したくなります。そのためのツールです。
gifdump.py
#!/usr/bin/env python3
INDENT = ' ' * 4
def read8(fp):
return fp.read(1)[0]
def read16(fp):
l, h = fp.read(2)
return (h << 8) | l
def print_header(fp):
SIGNATURE = b'GIF'
VERSION = (b'87a', b'89a')
signature = fp.read(3)
if signature != SIGNATURE:
raise Exception('%s: signature(%s) != %s' % (path, SIGNATURE, signature))
print('Signature:', signature)
version = fp.read(3)
if version not in VERSION:
raise Exception('%s: unknown version: %s' % (path, version))
print('Version :', version)
def print_logical_screen_descriptor(fp):
indent = INDENT
print('Logical Screen Descriptor:')
print(indent + 'Logical Screen Width :', read16(fp))
print(indent + 'Logical Screen Height:', read16(fp))
packed_fields = read8(fp)
size_of_global_color_table = packed_fields & 7
color_resolution = (packed_fields >> 4) & 7
global_color_table_flag = bool((packed_fields >> 7) & 1)
print(indent + 'Packed Fields: 0x%02x' % packed_fields)
print(indent * 2 + 'Size of Global Color Table: %d (%d)' %
(size_of_global_color_table, 2 << size_of_global_color_table))
print(indent * 2 + 'Sort Flag : %s' % bool((packed_fields >> 3) & 1))
print(indent * 2 + 'Color Resolution : %d (%d)' % (color_resolution, (2 << color_resolution)))
print(indent * 2 + 'Global Color Table Flag : %s' % global_color_table_flag)
print(indent + 'Background Color Index: %d' % read8(fp))
print(indent + 'Pixel Aspect Ratio : %d' % read8(fp))
if global_color_table_flag:
print_color('Global Color Table', size_of_global_color_table, fp, indent)
def print_descriptor(fp):
desc = read8(fp)
if desc == 0x2c:
return print_image_descriptor(fp)
if desc == 0x21:
return print_extension(fp)
if desc == 0x3b:
return print_trailer(fp)
raise Exception('unknown descriptor: 0x%02x' % desc)
def print_image_descriptor(fp):
indent = INDENT
print('Image Descriptor: descriptor=0x2c')
print(indent + 'Image Left Position:', read16(fp))
print(indent + 'Image Top Position:', read16(fp))
print(indent + 'Image Width :', read16(fp))
print(indent + 'Image Height :', read16(fp))
packed_fields = read8(fp)
size_of_local_color_table = packed_fields & 7
local_color_table_flag = bool((packed_fields >> 7) & 1)
print(indent + 'Packed Fields : 0x%02x' % packed_fields)
print(indent * 2 + 'Size of Local Color Table: %d (%d)' %
(size_of_local_color_table, 2 << size_of_local_color_table))
print(indent * 2 + 'Reserved : %s' % ((packed_fields >> 3) & 3))
print(indent * 2 + 'Sort Flag : %s' % bool((packed_fields >> 5) & 1))
print(indent * 2 + 'Interlace Flag : %s' % bool((packed_fields >> 6) & 1))
print(indent * 2 + 'Local Color Table Flag : %s' % local_color_table_flag)
if local_color_table_flag:
print_color('Local Color Table', size_of_local_color_table, fp, indent)
print('Table Based Image Data:')
print(indent + 'LZW minimum code size: %d' % read8(fp))
print_data_sub_blocks(fp, indent)
return True
def print_extension(fp):
label = read8(fp)
if label == 0xf9:
return print_graphic_control_extension(fp)
if label == 0xfe:
return print_commnet_extension(fp)
if label == 0x01:
return print_plain_text_extension(fp)
if label == 0xff:
return print_application_extension(fp)
print('Unknown Extension: descriptor=0x21, label=0x%02x' % label)
print_data_sub_blocks(fp, INDENT)
return True
def print_graphic_control_extension(fp):
indent = INDENT
indent2 = indent * 2
indent3 = indent * 3
print('Graphic Control Extension: descriptor=0x21, label=0xf9')
if read8(fp) != 4:
raise Exception('Invalid Graphic Control Extension')
print(indent + 'Data Sub-block: size=4')
packed_fields = read8(fp)
print(indent2 + 'Packed Fields : 0x%02x' % packed_fields)
print(indent3 + 'Reserved : %d' % (packed_fields & 7))
print(indent3 + 'Disposal Method : %d' % ((packed_fields >> 3) & 7))
print(indent3 + 'User Input Flag : %s' % bool((packed_fields >> 6) & 1))
print(indent3 + 'Transparent Color flag: %s' % bool((packed_fields >> 7) & 1))
print(indent2 + 'Delay Time : %d' % read16(fp))
print(indent2 + 'Transparent Color Indent: %d' % read8(fp))
if read8(fp) != 0:
raise Exception('Invalid Graphic Control Extension')
return True
def print_commnet_extension(fp):
indent = INDENT
print('Comment Extension: descriptor=0x21, label=0xfe')
print_data_sub_blocks(fp, indent)
def print_plain_text_extension(fp):
indent = INDENT
indent2 = indent * 2
print('Plain Text Extension: descriptor=0x21, label=0x01')
if read8(fp) != 12:
raise Exception('Invalid Plain Text Extension')
print(indent + 'Data Sub-block: size=12')
print(indent2 + 'Text Grid Left Position : %d' % read16(fp))
print(indent2 + 'Text Grid Top Position : %d' % read16(fp))
print(indent2 + 'Text Grid Width : %d' % read16(fp))
print(indent2 + 'Text Grid Height : %d' % read16(fp))
print(indent2 + 'Character Cell Width : %d' % read8(fp))
print(indent2 + 'Character Cell Height : %d' % read8(fp))
print(indent2 + 'Text Foreground Color Index: %d' % read8(fp))
print(indent2 + 'Text Background Color Index: %d' % read8(fp))
print_data_sub_blocks(fp, indent)
return True
def print_application_extension(fp):
indent = INDENT
indent2 = indent * 2
print('Application Extension: descriptor=0x21, label=0xff')
if read8(fp) != 11:
raise Exception('Invalid Application Extension')
print(indent + 'Data Sub-block: size=11')
identifier = fp.read(8)
print(indent2 + 'Application Identifier :', identifier)
authentication_code = fp.read(3)
print(indent2 + 'Application Authentication Code:', authentication_code)
print_data_sub_blocks(fp, indent)
return True
def print_trailer(fp):
print('Trailer: descriptor=0x3b')
return False
def print_color(prefix, ctsize, fp, indent):
print(indent + prefix + ':')
count = 2 << ctsize
for y in range(0, count, 4):
msg = []
for x in range(4):
n = y + x
if n >= count:
break
msg.append('(%3d,%3d,%3d)' % tuple(fp.read(3)))
print(indent * 2 + ('%02x: ' % y) + ' '.join(msg))
def print_data_sub_blocks(fp, indent):
while True:
count = read8(fp)
print(indent + 'Data Sub-block: size=%d' % count)
if count == 0:
break
for y in range(0, count, 16):
msg = []
for x in range(16):
n = y + x
if n >= count:
break
msg.append('%02x' % read8(fp))
print(indent * 2 + ('%02x: ' % y) + ' '.join(msg))
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('GIF')
args = parser.parse_args()
path = args.GIF
print('GIF:', path)
fp = open(path, 'rb')
print_header(fp)
print_logical_screen_descriptor(fp)
while print_descriptor(fp):
pass
テスト
こんな感じで出力します。
$ python3 gifdump.py sample.gif
GIF: sample.gif
Signature: b'GIF'
Version : b'89a'
Logical Screen Descriptor:
Logical Screen Width : 640
Logical Screen Height: 480
Packed Fields: 0xf7
Size of Global Color Table: 7 (256)
Sort Flag : False
Color Resolution : 7 (256)
Global Color Table Flag : True
Background Color Index: 0
Pixel Aspect Ratio : 49
Global Color Table:
00: ( 0, 0, 0) ( 1, 1, 1) ( 2, 2, 2) ( 3, 3, 3)
04: ( 4, 4, 4) ( 5, 5, 5) ( 6, 6, 6) ( 7, 7, 7)
08: ( 8, 8, 8) ( 9, 9, 9) ( 10, 10, 10) ( 11, 11, 11)
0c: ( 12, 12, 12) ( 13, 13, 13) ( 14, 14, 14) ( 15, 15, 15)
〜〜〜(略)〜〜〜
f0: (240,240,240) (241,241,241) (242,242,242) (243,243,243)
f4: (244,244,244) (245,245,245) (246,246,246) (247,247,247)
f8: (248,248,248) (249,249,249) (250,250,250) (251,251,251)
fc: (252,252,252) (253,253,253) (254,254,254) (255,255,255)
Image Descriptor: descriptor=0x2c
Image Left Position: 0
Image Top Position: 0
Image Width : 640
Image Height : 480
Packed Fields : 0x00
Size of Local Color Table: 0 (2)
Reserved : 0
Sort Flag : False
Interlace Flag : False
Local Color Table Flag : False
Table Based Image Data:
LZW minimum code size: 8
Data Sub-block: size=255
00: 00 01 08 1c 48 b0 a0 c1 83 08 13 2a 5c c8 b0 a1
10: c3 87 10 23 4a 9c 48 b1 a2 c5 8b 18 33 6a dc c8
20: b1 a3 c7 8f 20 43 8a 1c b9 30 80 c9 93 28 53 aa
30: 5c c9 b2 a5 cb 97 30 63 ca 9c 49 b3 a6 cd 9b 38
〜〜〜(略)〜〜〜
c0: 2b d8 c1 12 b6 b0 86 3d 2c 62 13 ab d8 c5 32 b6
d0: b1 8e 7d 2c 64 23 2b d9 c9 de 75 25 96 bd 2c 66
e0: 33 ab d9 cd 72 b6 b3 9e fd 2c 68 43 2b da d1 92
f0: b6 b4 a6 3d 2d 6a 53 ab da d5 b2 b6 b5 ae 7d
Data Sub-block: size=14
00: 2d 6c 63 2b db d9 d2 b6 b6 b6 fd 6c 40 00
Data Sub-block: size=0
Trailer: descriptor=0x3b