0
0

More than 1 year has passed since last update.

GIF データを確認する

Posted at

デバッグ用のツール

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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0