Python |GIF 解析与构建(1):初步解析

发布于:2025-05-22 ⋅ 阅读:(17) ⋅ 点赞:(0)

Python |GIF 解析与构建(1):初步解析

目录

Python |GIF 解析与构建(1):初步解析

一、GIF 格式概述

二、GIF 文件结构解析

1. 文件头(Header)

2. 逻辑屏幕描述块(Logical Screen Descriptor)

3. 全局调色板(Global Color Table)

4. 图像数据块(Image Data)

(1)图像标识符(Image Descriptor)

(2)局部调色板(Local Color Table)

(3)图像数据(Image Data)

5. 动画控制块(Graphic Control Extension,仅 GIF89a 支持)

6. 文件尾(Trailer)


一、GIF 格式概述

GIF(Graphics Interchange Format)是一种基于 LZW 压缩算法 的位图图像格式,诞生于 1987 年,至今仍是网页动画、表情包等场景的常用格式。其核心特点包括:

  • 支持动画:通过多帧图像按顺序播放实现动态效果。
  • 索引色模式:最多包含 256 种颜色(通过调色板索引映射),压缩效率高但色彩表现有限。
  • 无损压缩:LZW 算法保证图像质量不损失,适合线条图、图标等简单图形。

二、GIF 文件结构解析

GIF 格式采用 二进制分块结构,由多个独立的数据块组成。以下是最核心的基础结构(以 GIF89a 版本为例):

1. 文件头(Header)

  • 签名(Signature):3 字节固定值 0x47 0x49 0x46(即 "GIF" 字符),用于标识文件类型。
  • 版本号(Version):3 字节,常见值为 "87a" 或 "89a"(后者支持动画和透明色)。

2. 逻辑屏幕描述块(Logical Screen Descriptor)

定义图像的显示区域和全局属性,结构如下:

字段 长度(字节) 说明
屏幕宽度 2 以像素为单位的宽度(低字节在前,高字节在后)
屏幕高度 2 以像素为单位的高度(低字节在前,高字节在后)
标志位 1 包含调色板信息(如是否存在全局调色板、颜色深度、是否支持透明色等)
背景色索引 1 全局背景色在调色板中的索引(0 表示无背景色)
像素 aspect 比 1 像素宽高比(通常为 0 表示默认值)

示例
若标志位为 0x87(二进制 10000111),表示:

  • 存在全局调色板(最高位为 1),调色板大小为 2(3+1)=16 色(低 3 位 011 表示索引位数为 3+1)。

3. 全局调色板(Global Color Table)

可选块,当逻辑屏幕描述块的标志位中全局调色板存在时出现。结构为:

  • 由连续的 3*N 字节组成,每个颜色占 3 字节(R、G、B 分量,各 1 字节),N 为颜色数(2n,n 由标志位确定)。

4. 图像数据块(Image Data)

每个图像数据块对应一帧画面,结构如下:

(1)图像标识符(Image Descriptor)
字段 长度(字节) 说明
标识符 1 固定值 0x2C(逗号 ,),表示图像数据开始
左偏移量 2 图像在屏幕中的水平起始位置(低字节在前)
顶偏移量 2 图像在屏幕中的垂直起始位置(低字节在前)
图像宽度 2 图像宽度(像素)
图像高度 2 图像高度(像素)
标志位 1 包含局部调色板信息、是否为透明色、是否需要交错显示等
(2)局部调色板(Local Color Table)

可选块,若标志位中指定存在局部调色板,则结构与全局调色板一致,但仅作用于当前图像帧。

(3)图像数据(Image Data)
  • 压缩数据块:采用 LZW 压缩算法,先存储 初始代码大小(Initial Code Size)(1 字节,表示初始编码位数,通常为 7 或 8),随后是 压缩数据流(由多个数据块组成,每个数据块以 0x00 结尾)。
  • 像素解码:通过 LZW 算法解压缩后,得到每个像素的颜色索引值,通过局部或全局调色板映射为 RGB 颜色。

5. 动画控制块(Graphic Control Extension,仅 GIF89a 支持)

用于定义动画帧的播放规则,结构如下:

字段 长度(字节) 说明
扩展标识符 1 固定值 0x21(感叹号 !
块标识符 1 固定值 0xF9(图形控制扩展)
块大小 1 固定值 0x04
标志位 1 包含透明色索引是否有效、动画播放方式(如是否循环)等
延迟时间 2 帧停留时间(单位为 1/100 秒,低字节在前)
透明色索引 1 可选,透明像素的颜色索引
结束符 1 固定值 0x00

6. 文件尾(Trailer)

1 字节固定值 0x3B(分号 ;),标识文件结束。

from PIL import Image
import struct

dict_ex = {"ff":"扩展应用块","f9":'图形控制扩展'}
# 读取文件
gif_path = "1.gif"
for _ in range(2):
    try:
        with open(gif_path, 'rb') as f:
            data = f.read()
        break
    except:
        # 创建全白帧 50x50
        white_frame = Image.new('RGB', (5, 5), color=(255, 255, 255))
        # 创建全黑帧 50x50
        black_frame = Image.new('RGB', (5, 5), color=(0, 0, 0))
        # 保存为无限循环的GIF动画
        white_frame.save(
            gif_path,
            save_all=True,
            append_images=[black_frame],
            duration=200,
            loop=0
        )


print(data)
print(data.hex(' '))
# 步进
start_number = 0
end_number = 0


# 步数处理
def deal(end_add):
    global start_number, end_number
    start_number = end_number
    end_number += end_add
    return data[start_number:end_number]

# 解码
def lzw_decode(compressed):
    # 初始化字典
    dictionary = {i: bytes([i]) for i in range(256)}
    next_code = 258
    result = bytearray()

    # 处理压缩数据
    buffer = 0
    bits_in_buffer = 0
    code_size = 9
    prev_code = None

    for byte in compressed:
        buffer |= byte << bits_in_buffer
        bits_in_buffer += 8

        while bits_in_buffer >= code_size:
            code = buffer & ((1 << code_size) - 1)
            buffer >>= code_size
            bits_in_buffer -= code_size

            if code == 256:  # 清除码
                dictionary = {i: bytes([i]) for i in range(256)}
                next_code = 258
                code_size = 9
                prev_code = None
                continue
            elif code == 257:  # 结束码
                return bytes(result)

            if prev_code is None:
                result.extend(dictionary[code])
                prev_code = code
                continue

            if code in dictionary:
                entry = dictionary[code]
                result.extend(entry)
                dictionary[next_code] = dictionary[prev_code] + entry[:1]
            else:
                entry = dictionary[prev_code] + dictionary[prev_code][:1]
                result.extend(entry)
                dictionary[next_code] = entry

            next_code += 1
            prev_code = code

            if next_code >= (1 << code_size) and code_size < 12:
                code_size += 1

    return bytes(result)

# 压缩
def lzw_decompress(compressed):
    dictionary = {i: bytes([i]) for i in range(256)}
    next_code = 258
    result = bytearray()
    buffer = 0
    bits = 0
    code_size = 9
    prev = None

    for byte in compressed:
        buffer |= byte << bits
        bits += 8
        while bits >= code_size:
            code = buffer & ((1 << code_size) - 1)
            buffer >>= code_size
            bits -= code_size

            if code == 256:
                dictionary = {i: bytes([i]) for i in range(256)}
                next_code = 258
                code_size = 9
                prev = None
                continue
            elif code == 257:
                return bytes(result)

            if prev is None:
                result.extend(dictionary[code])
                prev = code
                continue

            if code in dictionary:
                entry = dictionary[code]
                result.extend(entry)
                dictionary[next_code] = dictionary[prev] + entry[:1]
            else:
                entry = dictionary[prev] + dictionary[prev][:1]
                result.extend(entry)
                dictionary[next_code] = entry

            next_code += 1
            prev = code

            if next_code > (1 << code_size) - 1 and code_size < 12:
                code_size += 1

    return bytes(result)


print("开头GIF89a")
bytes1 = deal(6)
print(bytes1.decode('ascii', errors='replace'))
print(bytes1.hex(' '))

print("画布宽度")
bytes2 = deal(2)
print(struct.unpack('<H', bytes2)[0])
print(bytes2.hex(' '))

print("画布长度")
bytes3 = deal(2)
print(struct.unpack('<H', bytes3)[0])
print(bytes3.hex(' '))

print("画布颜色位")
bytes4 = deal(1)
print(bool(bytes4[0] & 0b10000000))
print(bytes4.hex(' '))
"""
假设:
m = 1(存在全局颜色表);
cr = 3(颜色深度为 3,二进制 011);
s = 0(不使用分类标志);
pixel = 4(全局颜色列表大小为 4,二进制 100)。
计算各字段的位位置
m 占位 7 → 需左移 7 位(m << 7);
cr 占位 6-4 → 需左移 4 位(cr << 4);
s 占位 3 → 需左移 3 位(s << 3);
pixel 占位 2-0 → 无需位移(直接取 pixel)
"""

print("背景颜色索引")
bytes5 = deal(1)
print(bytes5.hex(' '))

print("像素宽高比")
bytes6 = deal(1)
print(bytes6.hex(' '))  # 默认0不使用

print("颜色计算")
color_number = 2 ** ((bytes4[0] & 0b00000111) + 1)  # 颜色数量不够需要补齐  解包采用掩码提取 +1是容纳更多颜色 而且因为2^0=1 一种颜色没有意义
print(f"颜色数量:{color_number}")
bytes7 = deal(3 * color_number)
print(bytes7.hex(' '))

print("扩展引导符号")
bytes8 = deal(1)
print(bytes8.decode('ascii', errors='replace'))
print(bytes8.hex(' '))


print("扩展类型")
bytes9 = deal(1)
print(dict_ex[bytes9.hex(' ')])
print(bytes9.hex(' '))

print("块长度")
bytes10 = deal(1)
print(struct.unpack('<B', bytes10)[0])
print(bytes10.hex(' '))

print("扩展应用标识符")
bytes11 = deal(struct.unpack('<B', bytes10)[0])
print(bytes11.decode('ascii', errors='replace'))
print(bytes11.hex(' '))
# 扩展应用ff 0b     4e 45 54 53 43 41 50 45 32 2e 30 03 01      00 00【循环次数】 00
"""
以 ASCII 编码表示应用程序名称:NETSCAPE2.0。
这是 Netscape 浏览器定义的 GIF 动画循环控制扩展。
"""

print("子块长度")
bytes12 = deal(1)
print(struct.unpack('<B', bytes12)[0])
print(bytes12.hex(' '))

print("循环类型")
bytes13 = deal(1)
print(bytes13.hex(' '))

print("循环参数")
bytes14 = deal(2)
print(bytes14.hex(' '))
"""
0x00 00 表示 无限循环。
若为 0x01 00,则表示循环 1 次(播放 2 次后停止)。
若为 0x05 00,则表示循环 5 次(播放 6 次后停止)。
"""

print("块结束符")
bytes15 = deal(1)
print(bytes15.hex(' '))

print("扩展引导符号")
bytes16 = deal(1)
print(bytes16.decode('ascii', errors='replace'))
print(bytes16.hex(' '))

print("扩展类型")
bytes17 = deal(1)
print(dict_ex[bytes17.hex(' ')])
print(bytes17.hex(' '))

print("块长度")
bytes18 = deal(1)
print(struct.unpack('<B', bytes18)[0])
print(bytes18.hex(' '))

print("标志位")
bytes19 = deal(1)
print(bytes19.hex(' '))
"""
- 第 7 位(最高位):透明度标志(0 = 关闭)
- 第 6 位:用户输入标志(0 = 无需等待输入)
- 第 3-5 位: disposal 方法(0 = 不指定,默认保留)
- 第 0-2 位:保留位(0)
"""
print("延迟时间")
bytes20 = deal(2)
print(struct.unpack('<H', bytes20)[0]*0.01,"秒")

print("透明色索引")
bytes20 = deal(1)
print(bytes20.hex(' '))
"""
若标志位启用透明度(第 7 位为 1),此处为有效颜色索引。
"""
print("块结束符")
bytes21 = deal(1)
print(bytes21.hex(' '))

print("图像分隔符 开始")
bytes22 = deal(1)
print(bytes22.hex(' '))

print("图像左偏移量")
bytes23 = deal(2)
print(struct.unpack('<H', bytes23)[0])
print(bytes23.hex(' '))

print("图像顶偏移量")
bytes24 = deal(2)
print(struct.unpack('<H', bytes24)[0])
print(bytes24.hex(' '))


print("画布宽度")
bytes25 = deal(2)
print(struct.unpack('<H', bytes25)[0])
print(bytes25.hex(' '))

print("画布长度")
bytes26 = deal(2)
print(struct.unpack('<H', bytes26)[0])
print(bytes26.hex(' '))

print("局部颜色表标志")
bytes27 = deal(1)
print(bytes27.hex(' '))
"""
- 第 7 位:局部颜色表标志(0 = 无局部颜色表)
- 第 6 位:交织标志(0 = 非交织)
- 第 5 位:排序标志(0 = 未排序)
- 第 3-4 位:保留位(0)
- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)
"""

print("压缩位数")
bytes27 = deal(1)
print(struct.unpack('<B', bytes27)[0])
print(bytes27.hex(' '))

print("子块字节")
bytes28 = deal(1)
print(struct.unpack('<B', bytes28)[0])
print(bytes28.hex(' '))

print("子块数据")
bytes29 = deal(struct.unpack('<B', bytes28)[0])
print(bytes29.hex(' '))
print(bytes29)
print(lzw_decode(bytes29))

print("终止符")
bytes30 = deal(1)
print(bytes30.hex(' '))

print("扩展引导符号")
bytes31 = deal(1)
print(bytes31.decode('ascii', errors='replace'))
print(bytes31.hex(' '))

print("扩展类型")
bytes32 = deal(1)
print(dict_ex[bytes32.hex(' ')])
print(bytes32.hex(' '))

print("块长度")
bytes33 = deal(1)
print(struct.unpack('<B', bytes33)[0])
print(bytes33.hex(' '))

print("标志位")
bytes34 = deal(1)
print(bytes34.hex(' '))
"""
- 第 7 位(最高位):透明度标志(0 = 关闭)
- 第 6 位:用户输入标志(0 = 无需等待输入)
- 第 3-5 位: disposal 方法(0 = 不指定,默认保留)
- 第 0-2 位:保留位(0)
"""
print("延迟时间")
bytes35 = deal(2)
print(struct.unpack('<H', bytes35)[0]*0.01,"秒")
print(bytes35.hex(' '))

print("透明色索引")
bytes36 = deal(1)
print(bytes36.hex(' '))
"""
若标志位启用透明度(第 7 位为 1),此处为有效颜色索引。
"""
print("块结束符")
bytes37 = deal(1)
print(bytes37.hex(' '))


print("图像分隔符 开始")
bytes38 = deal(1)
print(bytes38.hex(' '))

print("图像左偏移量")
bytes39 = deal(2)
print(struct.unpack('<H', bytes39)[0])
print(bytes39.hex(' '))

print("图像顶偏移量")
bytes40 = deal(2)
print(struct.unpack('<H', bytes40)[0])
print(bytes40.hex(' '))


print("画布宽度")
bytes41 = deal(2)
print(struct.unpack('<H', bytes41)[0])
print(bytes41.hex(' '))

print("画布长度")
bytes42 = deal(2)
print(struct.unpack('<H', bytes42)[0])
print(bytes42.hex(' '))

print("局部颜色表标志")
bytes43 = deal(1)
print(bytes43.hex(' '))
"""
- 第 7 位:局部颜色表标志(0 = 无局部颜色表)
- 第 6 位:交织标志(0 = 非交织)
- 第 5 位:排序标志(0 = 未排序)
- 第 3-4 位:保留位(0)
- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)
"""
color_number = 2 ** ((bytes43[0] & 0b00000111) + 1)  # 颜色数量不够需要补齐  解包采用掩码提取 +1是容纳更多颜色 而且因为2^0=1 一种颜色没有意义
print(f"颜色数量:{color_number}")
bytes44 = deal(3 * color_number)
print(bytes44.hex(' '))

print("压缩位数")
bytes45 = deal(1)
print(struct.unpack('<B', bytes45)[0])
print(bytes45.hex(' '))

print("子块字节")
bytes46 = deal(1)
print(struct.unpack('<B', bytes46)[0])
print(bytes46.hex(' '))

print("子块数据")
bytes47 = deal(struct.unpack('<B', bytes46)[0])
print(bytes47.hex(' '))
print(bytes47)
print(lzw_decode(bytes47))

print("终止符")
bytes48 = deal(1)
print(bytes48.hex(' '))

print("结束")
bytes49 = deal(1)
print(bytes49.hex(' '))
print(bytes49)

from PIL import Image
import struct

dict_ex = {"ff":"扩展应用块","f9":'图形控制扩展'}
# 读取文件
gif_path = "1.gif"
for _ in range(2):
    try:
        with open(gif_path, 'rb') as f:
            data = f.read()
        break
    except:
        # 创建全白帧 50x50
        white_frame = Image.new('RGB', (5, 5), color=(255, 255, 255))
        # 创建全黑帧 50x50
        black_frame = Image.new('RGB', (5, 5), color=(0, 0, 0))
        # 保存为无限循环的GIF动画
        white_frame.save(
            gif_path,
            save_all=True,
            append_images=[black_frame],
            duration=200,
            loop=0
        )


print(data)
print(data.hex(' '))
# 步进
start_number = 0
end_number = 0


# 步数处理
def deal(end_add):
    global start_number, end_number
    start_number = end_number
    end_number += end_add
    return data[start_number:end_number]

# 解码
def lzw_decode(compressed):
    # 初始化字典
    dictionary = {i: bytes([i]) for i in range(256)}
    next_code = 258
    result = bytearray()

    # 处理压缩数据
    buffer = 0
    bits_in_buffer = 0
    code_size = 9
    prev_code = None

    for byte in compressed:
        buffer |= byte << bits_in_buffer
        bits_in_buffer += 8

        while bits_in_buffer >= code_size:
            code = buffer & ((1 << code_size) - 1)
            buffer >>= code_size
            bits_in_buffer -= code_size

            if code == 256:  # 清除码
                dictionary = {i: bytes([i]) for i in range(256)}
                next_code = 258
                code_size = 9
                prev_code = None
                continue
            elif code == 257:  # 结束码
                return bytes(result)

            if prev_code is None:
                result.extend(dictionary[code])
                prev_code = code
                continue

            if code in dictionary:
                entry = dictionary[code]
                result.extend(entry)
                dictionary[next_code] = dictionary[prev_code] + entry[:1]
            else:
                entry = dictionary[prev_code] + dictionary[prev_code][:1]
                result.extend(entry)
                dictionary[next_code] = entry

            next_code += 1
            prev_code = code

            if next_code >= (1 << code_size) and code_size < 12:
                code_size += 1

    return bytes(result)

# 压缩
def lzw_decompress(compressed):
    dictionary = {i: bytes([i]) for i in range(256)}
    next_code = 258
    result = bytearray()
    buffer = 0
    bits = 0
    code_size = 9
    prev = None

    for byte in compressed:
        buffer |= byte << bits
        bits += 8
        while bits >= code_size:
            code = buffer & ((1 << code_size) - 1)
            buffer >>= code_size
            bits -= code_size

            if code == 256:
                dictionary = {i: bytes([i]) for i in range(256)}
                next_code = 258
                code_size = 9
                prev = None
                continue
            elif code == 257:
                return bytes(result)

            if prev is None:
                result.extend(dictionary[code])
                prev = code
                continue

            if code in dictionary:
                entry = dictionary[code]
                result.extend(entry)
                dictionary[next_code] = dictionary[prev] + entry[:1]
            else:
                entry = dictionary[prev] + dictionary[prev][:1]
                result.extend(entry)
                dictionary[next_code] = entry

            next_code += 1
            prev = code

            if next_code > (1 << code_size) - 1 and code_size < 12:
                code_size += 1

    return bytes(result)


print("开头GIF89a")
bytes1 = deal(6)
print(bytes1.decode('ascii', errors='replace'))
print(bytes1.hex(' '))

print("画布宽度")
bytes2 = deal(2)
print(struct.unpack('<H', bytes2)[0])
print(bytes2.hex(' '))

print("画布长度")
bytes3 = deal(2)
print(struct.unpack('<H', bytes3)[0])
print(bytes3.hex(' '))

print("画布颜色位")
bytes4 = deal(1)
print(bool(bytes4[0] & 0b10000000))
print(bytes4.hex(' '))
"""
假设:
m = 1(存在全局颜色表);
cr = 3(颜色深度为 3,二进制 011);
s = 0(不使用分类标志);
pixel = 4(全局颜色列表大小为 4,二进制 100)。
计算各字段的位位置
m 占位 7 → 需左移 7 位(m << 7);
cr 占位 6-4 → 需左移 4 位(cr << 4);
s 占位 3 → 需左移 3 位(s << 3);
pixel 占位 2-0 → 无需位移(直接取 pixel)
"""

print("背景颜色索引")
bytes5 = deal(1)
print(bytes5.hex(' '))

print("像素宽高比")
bytes6 = deal(1)
print(bytes6.hex(' '))  # 默认0不使用

print("颜色计算")
color_number = 2 ** ((bytes4[0] & 0b00000111) + 1)  # 颜色数量不够需要补齐  解包采用掩码提取 +1是容纳更多颜色 而且因为2^0=1 一种颜色没有意义
print(f"颜色数量:{color_number}")
bytes7 = deal(3 * color_number)
print(bytes7.hex(' '))

print("扩展引导符号")
bytes8 = deal(1)
print(bytes8.decode('ascii', errors='replace'))
print(bytes8.hex(' '))


print("扩展类型")
bytes9 = deal(1)
print(dict_ex[bytes9.hex(' ')])
print(bytes9.hex(' '))

print("块长度")
bytes10 = deal(1)
print(struct.unpack('<B', bytes10)[0])
print(bytes10.hex(' '))

print("扩展应用标识符")
bytes11 = deal(struct.unpack('<B', bytes10)[0])
print(bytes11.decode('ascii', errors='replace'))
print(bytes11.hex(' '))
# 扩展应用ff 0b     4e 45 54 53 43 41 50 45 32 2e 30 03 01      00 00【循环次数】 00
"""
以 ASCII 编码表示应用程序名称:NETSCAPE2.0。
这是 Netscape 浏览器定义的 GIF 动画循环控制扩展。
"""

print("子块长度")
bytes12 = deal(1)
print(struct.unpack('<B', bytes12)[0])
print(bytes12.hex(' '))

print("循环类型")
bytes13 = deal(1)
print(bytes13.hex(' '))

print("循环参数")
bytes14 = deal(2)
print(bytes14.hex(' '))
"""
0x00 00 表示 无限循环。
若为 0x01 00,则表示循环 1 次(播放 2 次后停止)。
若为 0x05 00,则表示循环 5 次(播放 6 次后停止)。
"""

print("块结束符")
bytes15 = deal(1)
print(bytes15.hex(' '))

print("扩展引导符号")
bytes16 = deal(1)
print(bytes16.decode('ascii', errors='replace'))
print(bytes16.hex(' '))

print("扩展类型")
bytes17 = deal(1)
print(dict_ex[bytes17.hex(' ')])
print(bytes17.hex(' '))

print("块长度")
bytes18 = deal(1)
print(struct.unpack('<B', bytes18)[0])
print(bytes18.hex(' '))

print("标志位")
bytes19 = deal(1)
print(bytes19.hex(' '))
"""
- 第 7 位(最高位):透明度标志(0 = 关闭)
- 第 6 位:用户输入标志(0 = 无需等待输入)
- 第 3-5 位: disposal 方法(0 = 不指定,默认保留)
- 第 0-2 位:保留位(0)
"""
print("延迟时间")
bytes20 = deal(2)
print(struct.unpack('<H', bytes20)[0]*0.01,"秒")

print("透明色索引")
bytes20 = deal(1)
print(bytes20.hex(' '))
"""
若标志位启用透明度(第 7 位为 1),此处为有效颜色索引。
"""
print("块结束符")
bytes21 = deal(1)
print(bytes21.hex(' '))

print("图像分隔符 开始")
bytes22 = deal(1)
print(bytes22.hex(' '))

print("图像左偏移量")
bytes23 = deal(2)
print(struct.unpack('<H', bytes23)[0])
print(bytes23.hex(' '))

print("图像顶偏移量")
bytes24 = deal(2)
print(struct.unpack('<H', bytes24)[0])
print(bytes24.hex(' '))


print("画布宽度")
bytes25 = deal(2)
print(struct.unpack('<H', bytes25)[0])
print(bytes25.hex(' '))

print("画布长度")
bytes26 = deal(2)
print(struct.unpack('<H', bytes26)[0])
print(bytes26.hex(' '))

print("局部颜色表标志")
bytes27 = deal(1)
print(bytes27.hex(' '))
"""
- 第 7 位:局部颜色表标志(0 = 无局部颜色表)
- 第 6 位:交织标志(0 = 非交织)
- 第 5 位:排序标志(0 = 未排序)
- 第 3-4 位:保留位(0)
- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)
"""

print("压缩位数")
bytes27 = deal(1)
print(struct.unpack('<B', bytes27)[0])
print(bytes27.hex(' '))

print("子块字节")
bytes28 = deal(1)
print(struct.unpack('<B', bytes28)[0])
print(bytes28.hex(' '))

print("子块数据")
bytes29 = deal(struct.unpack('<B', bytes28)[0])
print(bytes29.hex(' '))
print(bytes29)
print(lzw_decode(bytes29))

print("终止符")
bytes30 = deal(1)
print(bytes30.hex(' '))

print("扩展引导符号")
bytes31 = deal(1)
print(bytes31.decode('ascii', errors='replace'))
print(bytes31.hex(' '))

print("扩展类型")
bytes32 = deal(1)
print(dict_ex[bytes32.hex(' ')])
print(bytes32.hex(' '))

print("块长度")
bytes33 = deal(1)
print(struct.unpack('<B', bytes33)[0])
print(bytes33.hex(' '))

print("标志位")
bytes34 = deal(1)
print(bytes34.hex(' '))
"""
- 第 7 位(最高位):透明度标志(0 = 关闭)
- 第 6 位:用户输入标志(0 = 无需等待输入)
- 第 3-5 位: disposal 方法(0 = 不指定,默认保留)
- 第 0-2 位:保留位(0)
"""
print("延迟时间")
bytes35 = deal(2)
print(struct.unpack('<H', bytes35)[0]*0.01,"秒")
print(bytes35.hex(' '))

print("透明色索引")
bytes36 = deal(1)
print(bytes36.hex(' '))
"""
若标志位启用透明度(第 7 位为 1),此处为有效颜色索引。
"""
print("块结束符")
bytes37 = deal(1)
print(bytes37.hex(' '))


print("图像分隔符 开始")
bytes38 = deal(1)
print(bytes38.hex(' '))

print("图像左偏移量")
bytes39 = deal(2)
print(struct.unpack('<H', bytes39)[0])
print(bytes39.hex(' '))

print("图像顶偏移量")
bytes40 = deal(2)
print(struct.unpack('<H', bytes40)[0])
print(bytes40.hex(' '))


print("画布宽度")
bytes41 = deal(2)
print(struct.unpack('<H', bytes41)[0])
print(bytes41.hex(' '))

print("画布长度")
bytes42 = deal(2)
print(struct.unpack('<H', bytes42)[0])
print(bytes42.hex(' '))

print("局部颜色表标志")
bytes43 = deal(1)
print(bytes43.hex(' '))
"""
- 第 7 位:局部颜色表标志(0 = 无局部颜色表)
- 第 6 位:交织标志(0 = 非交织)
- 第 5 位:排序标志(0 = 未排序)
- 第 3-4 位:保留位(0)
- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)
"""
color_number = 2 ** ((bytes43[0] & 0b00000111) + 1)  # 颜色数量不够需要补齐  解包采用掩码提取 +1是容纳更多颜色 而且因为2^0=1 一种颜色没有意义
print(f"颜色数量:{color_number}")
bytes44 = deal(3 * color_number)
print(bytes44.hex(' '))

print("压缩位数")
bytes45 = deal(1)
print(struct.unpack('<B', bytes45)[0])
print(bytes45.hex(' '))

print("子块字节")
bytes46 = deal(1)
print(struct.unpack('<B', bytes46)[0])
print(bytes46.hex(' '))

print("子块数据")
bytes47 = deal(struct.unpack('<B', bytes46)[0])
print(bytes47.hex(' '))
print(bytes47)
print(lzw_decode(bytes47))

print("终止符")
bytes48 = deal(1)
print(bytes48.hex(' '))

print("结束")
bytes49 = deal(1)
print(bytes49.hex(' '))
print(bytes49)


网站公告

今日签到

点亮在社区的每一天
去签到