solution_248f.py

#!/usr/bin/python3
# ===============================================================
# interpret/display GIF file header information
#
# en.wikipedia.org/wiki/GIF
#
# Note: test file mona_lisa_small_a.gif contains only one image
# ===============================================================

import hex_dump as hxd

# ---------------------------------------------------------------
# ---- give a color table index, display its red,gree,blue
# ---- values
# ---------------------------------------------------------------

def display_color_table_pixel(gif_bytes:str,off:int,idx:int) -> None:

    r = int.from_bytes(gif_bytes[idx  :idx+1],'little')
    g = int.from_bytes(gif_bytes[idx+1:idx+2],'little')
    b = int.from_bytes(gif_bytes[idx+2:idx+3],'little')

    print(f'[{off:02X}]            = {r:02X},{g:02X},{b:02X}')

    return
    
# ---------------------------------------------------------------
# ---- display gif file header
# ---------------------------------------------------------------

def display_gif_header(file_path,gif_bytes:str) -> None:

    print()
    print('---- gif Header -------------------------------')

    char1            = str(gif_bytes[0:6].decode('UTF-8'))
    width            = int.from_bytes(gif_bytes[6:8],'little')
    height           = int.from_bytes(gif_bytes[8:10],'little')
    gct              = int.from_bytes(gif_bytes[10:12],'little')
    bak_color_index  = int.from_bytes(gif_bytes[12:13],'little')
    aspect_ratio     = int.from_bytes(gif_bytes[13:14],'little')
    
    print(f'file            = {file_path}')
    print(f'width           = {width}')
    print(f'height          = {height}')
    print(f'GCT             = {gct:02X}')
    print(f'bg color index  = {bak_color_index}')
    print(f'aspect ratio    = {aspect_ratio}')

    print()
    print('---- Global Color Table - first six picels ----')

    idx    = 14         # byte array index (0xD)
    offset = 0          # color table offset (index)

    display_color_table_pixel(gif_bytes,offset,idx)
    idx    += 3
    offset += 1
    display_color_table_pixel(gif_bytes,offset,idx)
    idx    += 3
    offset += 1
    display_color_table_pixel(gif_bytes,offset,idx)
    idx    += 3
    offset += 1
    display_color_table_pixel(gif_bytes,offset,idx)
    idx    += 3
    offset += 1
    display_color_table_pixel(gif_bytes,offset,idx)
    idx    += 3
    offset += 1
    display_color_table_pixel(gif_bytes,offset,idx)
    print('...')
    print('...')

    print()
    print('---- Graphics Extention Block -----------------')

    idx = 781           # byte array index (0x30D)

    ext_blk = chr(int.from_bytes(gif_bytes[idx:idx+1],'little'))
    
    print(f'extension block = {ext_blk}')

    print()
    print('---- Image Descriptor -------------------------')

    idx = 789           # byte array index (0x315)

    desc   = chr(int.from_bytes(gif_bytes[idx:idx+1],'little'))
    nw_x   = int.from_bytes(gif_bytes[idx+1:idx+3],'little')
    nw_y   = int.from_bytes(gif_bytes[idx+3:idx+5],'little')
    width  = int.from_bytes(gif_bytes[idx+5:idx+7],'little')
    height = int.from_bytes(gif_bytes[idx+7:idx+9],'little')
    
    print(f'img descriptor  = {desc}')
    print(f'nw corner       = {nw_x},{nw_y}')
    print(f'width           = {width}')
    print(f'height          = {height}')

    print()
    print('---- Image Data -------------------------------')

    idx = 799           # byte array index (0x31F)

    lzwmin = int.from_bytes(gif_bytes[idx:idx+1],'little')

    # ---- skip the image data sub blocks

    idx          += 1
    sub_blk_count = 0
    
    while True:
        
        sub_blk_size = int.from_bytes(gif_bytes[idx:idx+1],'little')
        
        ##print(f'sub blk size = {sub_blk_size:02X}')

        sub_blk_count += 1
              
        if sub_blk_size == 0: break

        idx += sub_blk_size + 1  # skip image data sub block

    print(f'minimun LZW code {lzwmin}')
    print(f'last sub block size={sub_blk_size}')
    print(f'last sub block index={idx}')
    print(f'sub block count {sub_blk_count}')

# ---------------------------------------------------------------
# ---- open gif and read it directly into a byte array
# ----
# ---- It is not necessary to read in the complete file
# ---- if you are only accessing the headers. For example,
# ---- read the first 1000 bytes?
# ----
# ----   def load_gif_to_array(gif_file_path):
# ----       f = open(gif_file_path,'rb')
# ----       gif_bytes = f.read(1000)
# ----       close(f)
# ----       return gif_bytes
# ----
# ----   or
# ----
# ----   def load_gif_to_array(gif_file_path):
# ----       with open(gif_file_path,'rb') as f:
# ----           gif_bytes = f.read(1000)
# ----           return gif_bytes
# ----
# ---------------------------------------------------------------
    
def load_gif_to_array(gif_file_path:str) -> bytearray:

    with open(gif_file_path,'rb') as file:
        gif_bytes = file.read()
    return gif_bytes

# ---------------------------------------------------------------
# ---- main
# ---------------------------------------------------------------

if __name__ == '__main__':
    
    gif_file_paths = [ 'mona_lisa_small_a.gif',
                     ##'mona_lisa_small_b.gif'
                     ]

    for file_path in gif_file_paths:

        print()

        gif_bytes = load_gif_to_array(file_path)

        display_gif_header(file_path,gif_bytes)
        
    print()