Python |GIF 解析与构建(2):状态机解析
Python |GIF 解析与构建(2):状态机解析
目录
Python |GIF 解析与构建(2):状态机解析
引言
一、状态机概述
状态机的优势与改进方向
总结
引言
在《Python |GIF 解析与构建(1):初步解析》中,我们初步进行解析gif文件。
本文将深入探讨如何使用 ** 状态机(State Machine)** 实现 GIF 文件的解析,通过分阶段处理数据流,逐步提取关键信息(如版本、颜色表、图像数据等)。状态机的优势在于将复杂的解析逻辑拆解为多个可管理的状态,每个状态专注于处理特定的数据流片段,从而提高代码的可读性和可维护性。
一、状态机概述
GIF 文件由一系列区块(Block)组成,每个区块包含特定类型的数据(如文件头、颜色表、图像数据、扩展指令等)。状态机的核心思想是:根据当前解析进度,将程序划分为多个状态(State),每个状态负责解析某一类区块或数据片段,并根据输入数据决定下一步跳转的状态。
GIF 解析的主要状态包括:
- 开头(Header):验证 GIF 文件签名(
GIF87a
或GIF89a
)。 - 逻辑屏幕标识符(Logical Screen Descriptor):解析画布尺寸、颜色表标志等基础信息。
- 全局颜色表(Global Color Table):提取全局调色板(若存在)。
- 区块检测(Block Detection):识别后续区块类型(如图像标识符、扩展块等)。
- 扩展块处理(Extension Blocks):解析图形控制扩展(如动画延迟)、应用程序扩展(如循环参数)。
- 图像标识符(Image Descriptor):解析图像位置、尺寸、局部颜色表(若存在)及压缩数据。
- 结束符检测(Trailer Detection):验证文件结束符(
0x3B
)。
状态机的优势与改进方向
- 优势:
- 模块化:每个状态职责单一,便于调试和扩展。
- 容错性:通过状态转移控制数据流,可优雅处理无效数据(如提前终止解析)。
- 改进方向:
- 支持更多扩展块类型(如注释扩展、文本扩展)。
- 优化 LZW 解码性能(如使用更高效的数据结构存储字典)。
- 添加动画帧时序处理(结合图形控制扩展中的延迟时间)。
总结
本文通过状态机实现了 GIF 文件的逐步解析,将复杂的格式解析拆解为可管理的状态转移过程。状态机模型不仅适用于 GIF 解析,还可推广到其他二进制格式(如 PNG、BMP)的解析场景。下一篇文章将探讨如何基于状态机构建 GIF 文件,实现从像素数据到 GIF 动画的生成。
from PIL import Image import struct# 读取文件 gif_path = "1.gif" for _ in range(2):try:with open(gif_path, 'rb') as f:data = f.read()breakexcept:# 创建全白帧 50x50white_frame = Image.new('RGB', (5, 5), color=(255, 255, 255))# 创建全黑帧 50x50black_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)# 解码 def lzw_decode(compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256: # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257: # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif 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] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)# 压缩 def lzw_decompress(compressed):dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()buffer = 0bits = 0code_size = 9prev = Nonefor byte in compressed:buffer |= byte << bitsbits += 8while bits >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits -= code_sizeif code == 256:dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev = Nonecontinueelif code == 257:return bytes(result)if prev is None:result.extend(dictionary[code])prev = codecontinueif 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] = entrynext_code += 1prev = codeif next_code > (1 << code_size) - 1 and code_size < 12:code_size += 1return bytes(result)# 按照分块处理 print(data) print(data.hex(' '))# dict_ex = {"ff":"应用程序扩展","f9":'图形控制扩展'}# 状态机 class GIFDecode:def __init__(self):self.state = "开头" # 状态self.state_child = "开始" # 子状态self.global_color = [] # 全局颜色self.local_color_all = [] # 局部颜色(全部)self.local_color_one = [] # 局部颜色(单个)self.image_data = []self.version = "" # 版本self.buffer = bytearray() # 缓冲字节self.screen_width = 0 # 宽度self.screen_height = 0 # 长度self.has_global_color = 0 # 存在全局颜色表self.color_resolution = 3 # 颜色深度self.sort_flag = 0 # 分类标志self.global_color_table_size = 0 # 全局颜色数量self.bg_color_index = 0 # 背景颜色索引self.color_block_size = 0 # 颜色块长度self.pixel_aspect_ratio = 0 # 像素高宽比self.application_size = 0 # 应用程序扩展字节长度self.application_text = 0 # 应用程序扩展文本self.application_child_size = 0 # 应用程序扩展子块字节长度self.loop_type = 0 # 循环类型self.loop_parameter = 0 # 循环参数self.graphic_control_size = 0 # 图像控制字符长度self.graphic_control_sign_transparent = 0 # 图像控制标志位透明色self.graphic_control_sign_dispose = 0 # 图像控制标志位处置方法 # 0不使用 1把图形移去 2恢复到背景色 3恢复到先前状态 4-7自定义self.graphic_control_sign_input = 0 # 图像控制标志位用户输入self.graphic_control_sign_customize = 0 # 图像控制标志位自定义self.graphic_control_delay = 0 # 图像控制延迟时间self.graphic_control_sign_transparent_index = 0 # 图像控制透明色索引self.image_offset_x = 0 # 图像左偏移量 即X轴self.image_offset_y = 0 # 图像顶部偏移量 即Y轴self.image_width = 0 # 画布宽度self.image_length = 0 # 画布长度self.local_color_sign_number = 0 # 0-2颜色多少self.local_color_sign_retain = 0 # 3-4保留位self.image_local_color_sign_sort = 0 # 5排序self.local_color_sign_interwoven = 0 # 6交织self.local_color_sign_has_color_table = 0 # 7局部颜色表标志self.local_color_size = 0 # 局部颜色表字节self.image_compressed_bit = 0 # 压缩位self.image_block_size = 0 # 图像子块字节self.image_bytes_data = b"" # 图片压缩字节数据def feed(self, byte):print(self.buffer)if self.state == "开头":self.buffer.append(byte)if len(self.buffer) == 6:if self.buffer == b'GIF87a' or self.buffer == b'GIF89a':self.state = "逻辑屏幕标识符"self.version = self.buffer.decode('ascii')self.buffer.clear()else:raise ValueError(f"无效的GIF版本: {bytes(self.buffer)}")elif self.state == "逻辑屏幕标识符":self.buffer.append(byte)if len(self.buffer) == 7:# 解析逻辑屏幕描述符self.screen_width = int.from_bytes(self.buffer[0:2], 'little')self.screen_height = int.from_bytes(self.buffer[2:4], 'little')# 解析标志字节flags = self.buffer[4]"""假设: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)"""self.has_global_color = (flags & 0b10000000)self.color_resolution = (flags & 0b01110000)self.sort_flag = (flags & 0b00001000) != 0self.global_color_table_size = 2 ** ((flags & 0b00000111) + 1)self.bg_color_index = self.buffer[5]self.pixel_aspect_ratio = self.buffer[6]if self.has_global_color:self.state = "全局颜色表"self.color_block_size = self.global_color_table_size * 3self.buffer.clear()else:self.state = "区块检测"self.buffer.clear()elif self.state == "全局颜色表":self.buffer.append(byte)if len(self.buffer) == self.color_block_size:# 解析全局颜色表self.global_color = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.state = "区块检测"self.buffer.clear()elif self.state == "结束符检测":self.state_child = "开始" # 重置子状态if byte == 0x00:self.state = "区块检测"else:raise ValueError(f"无效的结束符: {bytes(byte)}")elif self.state == "区块检测":self.buffer.append(byte)if byte == 0x3B: # 检测结束return Trueif self.state_child == "开始":if len(self.buffer) == 1:if self.buffer[0] == 0x21:self.state_child = "拓展块"self.buffer.clear()elif self.buffer[0] == 0x2C:self.state = "图像标识符"self.state_child = "开始" # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state_child == "拓展块":if self.buffer[0] == 0xFF:self.state = "应用程序扩展"elif self.buffer[0] == 0xF9:self.state = "图形控制扩展"self.state_child = "开始" # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state == "应用程序扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.application_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.application_size:self.application_text = self.buffer[0:self.application_size].decode('ascii', errors='replace')self.state_child = "子块长度"self.buffer.clear()elif self.state_child == "子块长度":if len(self.buffer) == 1:self.application_child_size = struct.unpack('<B', self.buffer)[0]self.state_child = "子块解析"self.buffer.clear()elif self.state_child == "子块解析":if len(self.buffer) == self.application_child_size:self.loop_type = self.buffer[0]self.loop_parameter = self.buffer[1:2]self.state = "结束符检测"self.buffer.clear()elif self.state == "图形控制扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.graphic_control_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.graphic_control_size:sign = self.buffer[0]# 解析标志位self.graphic_control_sign_transparent = (sign & 0b00000001)self.graphic_control_sign_dispose = (sign & 0b00000010)self.graphic_control_sign_input = (sign & 0b00011100)self.graphic_control_sign_customize = (sign & 0b11100000)self.graphic_control_delay = struct.unpack('<H', self.buffer[1:3])[0] * 0.01self.graphic_control_sign_transparent_index = self.buffer[3]self.state = "结束符检测"self.buffer.clear()elif self.state == "图像标识符":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 9:self.image_offset_x = struct.unpack('<H', self.buffer[0:2])[0]self.image_offset_y = struct.unpack('<H', self.buffer[2:4])[0]self.image_width = struct.unpack('<H', self.buffer[4:6])[0]self.image_length = struct.unpack('<H', self.buffer[6:8])[0]sign = self.buffer[8]"""- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)- 第 3-4 位:保留位(0)- 第 5 位:排序标志(0 = 未排序)- 第 6 位:交织标志(0 = 非交织)- 第 7 位:局部颜色表标志(0 = 无局部颜色表)"""self.local_color_sign_number = (sign & 0b00000111)self.local_color_sign_retain = (sign & 0b00011000)self.image_local_color_sign_sort = (sign & 0b00100000)self.local_color_sign_interwoven = (sign & 0b01000000)self.local_color_sign_has_color_table = (sign & 0b10000000)# 检测是否存在局部颜色表格if self.local_color_sign_has_color_table == 128:self.local_color_size = 2 ** (self.local_color_sign_number + 1) * 3self.state_child = "获取局部颜色表"self.buffer.clear()else:self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "获取局部颜色表":if len(self.buffer) == self.local_color_size:# 获取局部颜色表放入局部颜色中self.local_color_one = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.local_color_all.append(self.local_color_one)self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "解析图像":if len(self.buffer) == 2:self.image_compressed_bit = self.buffer[0]self.image_block_size = self.buffer[1]self.state_child = "获取数据"self.buffer.clear()elif self.state_child == "获取数据":if len(self.buffer) == self.image_block_size:self.image_bytes_data += bytes(self.buffer) # 转换字节而非对象self.state_child = "检测获取结束"self.buffer.clear()elif self.state_child == "检测获取结束":if self.buffer[0] == 0x00:image_data = self.lzw_decode(self.image_bytes_data)if self.local_color_one: # 如果局部存在 则按照局部生成 如果局部不存在则按照全局生成self.image_data.append([self.local_color_one[image_data[i]] for i in range(len(image_data))])else:self.image_data.append([self.global_color[image_data[i]] for i in range(len(image_data))])self.state = "区块检测"self.state_child = "开始"# 重置数据self.image_bytes_data = b""self.local_color_one.clear()self.buffer.clear()else:self.image_block_size = self.buffer[0]self.state_child = "获取数据"self.buffer.clear()# 解码def lzw_decode(self, compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256: # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257: # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif 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] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)passdecoder = GIFDecode() for byte in data:decoder.feed(byte)print(decoder.version) print(decoder.image_data) print(decoder)
from PIL import Image
import struct# 读取文件
gif_path = "1.gif"
for _ in range(2):try:with open(gif_path, 'rb') as f:data = f.read()breakexcept:# 创建全白帧 50x50white_frame = Image.new('RGB', (5, 5), color=(255, 255, 255))# 创建全黑帧 50x50black_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)# 解码
def lzw_decode(compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256: # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257: # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif 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] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)# 压缩
def lzw_decompress(compressed):dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()buffer = 0bits = 0code_size = 9prev = Nonefor byte in compressed:buffer |= byte << bitsbits += 8while bits >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits -= code_sizeif code == 256:dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev = Nonecontinueelif code == 257:return bytes(result)if prev is None:result.extend(dictionary[code])prev = codecontinueif 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] = entrynext_code += 1prev = codeif next_code > (1 << code_size) - 1 and code_size < 12:code_size += 1return bytes(result)# 按照分块处理
print(data)
print(data.hex(' '))# dict_ex = {"ff":"应用程序扩展","f9":'图形控制扩展'}# 状态机
class GIFDecode:def __init__(self):self.state = "开头" # 状态self.state_child = "开始" # 子状态self.global_color = [] # 全局颜色self.local_color_all = [] # 局部颜色(全部)self.local_color_one = [] # 局部颜色(单个)self.image_data = []self.version = "" # 版本self.buffer = bytearray() # 缓冲字节self.screen_width = 0 # 宽度self.screen_height = 0 # 长度self.has_global_color = 0 # 存在全局颜色表self.color_resolution = 3 # 颜色深度self.sort_flag = 0 # 分类标志self.global_color_table_size = 0 # 全局颜色数量self.bg_color_index = 0 # 背景颜色索引self.color_block_size = 0 # 颜色块长度self.pixel_aspect_ratio = 0 # 像素高宽比self.application_size = 0 # 应用程序扩展字节长度self.application_text = 0 # 应用程序扩展文本self.application_child_size = 0 # 应用程序扩展子块字节长度self.loop_type = 0 # 循环类型self.loop_parameter = 0 # 循环参数self.graphic_control_size = 0 # 图像控制字符长度self.graphic_control_sign_transparent = 0 # 图像控制标志位透明色self.graphic_control_sign_dispose = 0 # 图像控制标志位处置方法 # 0不使用 1把图形移去 2恢复到背景色 3恢复到先前状态 4-7自定义self.graphic_control_sign_input = 0 # 图像控制标志位用户输入self.graphic_control_sign_customize = 0 # 图像控制标志位自定义self.graphic_control_delay = 0 # 图像控制延迟时间self.graphic_control_sign_transparent_index = 0 # 图像控制透明色索引self.image_offset_x = 0 # 图像左偏移量 即X轴self.image_offset_y = 0 # 图像顶部偏移量 即Y轴self.image_width = 0 # 画布宽度self.image_length = 0 # 画布长度self.local_color_sign_number = 0 # 0-2颜色多少self.local_color_sign_retain = 0 # 3-4保留位self.image_local_color_sign_sort = 0 # 5排序self.local_color_sign_interwoven = 0 # 6交织self.local_color_sign_has_color_table = 0 # 7局部颜色表标志self.local_color_size = 0 # 局部颜色表字节self.image_compressed_bit = 0 # 压缩位self.image_block_size = 0 # 图像子块字节self.image_bytes_data = b"" # 图片压缩字节数据def feed(self, byte):print(self.buffer)if self.state == "开头":self.buffer.append(byte)if len(self.buffer) == 6:if self.buffer == b'GIF87a' or self.buffer == b'GIF89a':self.state = "逻辑屏幕标识符"self.version = self.buffer.decode('ascii')self.buffer.clear()else:raise ValueError(f"无效的GIF版本: {bytes(self.buffer)}")elif self.state == "逻辑屏幕标识符":self.buffer.append(byte)if len(self.buffer) == 7:# 解析逻辑屏幕描述符self.screen_width = int.from_bytes(self.buffer[0:2], 'little')self.screen_height = int.from_bytes(self.buffer[2:4], 'little')# 解析标志字节flags = self.buffer[4]"""假设: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)"""self.has_global_color = (flags & 0b10000000)self.color_resolution = (flags & 0b01110000)self.sort_flag = (flags & 0b00001000) != 0self.global_color_table_size = 2 ** ((flags & 0b00000111) + 1)self.bg_color_index = self.buffer[5]self.pixel_aspect_ratio = self.buffer[6]if self.has_global_color:self.state = "全局颜色表"self.color_block_size = self.global_color_table_size * 3self.buffer.clear()else:self.state = "区块检测"self.buffer.clear()elif self.state == "全局颜色表":self.buffer.append(byte)if len(self.buffer) == self.color_block_size:# 解析全局颜色表self.global_color = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.state = "区块检测"self.buffer.clear()elif self.state == "结束符检测":self.state_child = "开始" # 重置子状态if byte == 0x00:self.state = "区块检测"else:raise ValueError(f"无效的结束符: {bytes(byte)}")elif self.state == "区块检测":self.buffer.append(byte)if byte == 0x3B: # 检测结束return Trueif self.state_child == "开始":if len(self.buffer) == 1:if self.buffer[0] == 0x21:self.state_child = "拓展块"self.buffer.clear()elif self.buffer[0] == 0x2C:self.state = "图像标识符"self.state_child = "开始" # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state_child == "拓展块":if self.buffer[0] == 0xFF:self.state = "应用程序扩展"elif self.buffer[0] == 0xF9:self.state = "图形控制扩展"self.state_child = "开始" # 重置子状态self.buffer.clear()else:raise ValueError(f"无效的区块: {bytes(self.buffer)}")elif self.state == "应用程序扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.application_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.application_size:self.application_text = self.buffer[0:self.application_size].decode('ascii', errors='replace')self.state_child = "子块长度"self.buffer.clear()elif self.state_child == "子块长度":if len(self.buffer) == 1:self.application_child_size = struct.unpack('<B', self.buffer)[0]self.state_child = "子块解析"self.buffer.clear()elif self.state_child == "子块解析":if len(self.buffer) == self.application_child_size:self.loop_type = self.buffer[0]self.loop_parameter = self.buffer[1:2]self.state = "结束符检测"self.buffer.clear()elif self.state == "图形控制扩展":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 1:self.graphic_control_size = struct.unpack('<B', self.buffer)[0]self.state_child = "解析"self.buffer.clear()elif self.state_child == "解析":if len(self.buffer) == self.graphic_control_size:sign = self.buffer[0]# 解析标志位self.graphic_control_sign_transparent = (sign & 0b00000001)self.graphic_control_sign_dispose = (sign & 0b00000010)self.graphic_control_sign_input = (sign & 0b00011100)self.graphic_control_sign_customize = (sign & 0b11100000)self.graphic_control_delay = struct.unpack('<H', self.buffer[1:3])[0] * 0.01self.graphic_control_sign_transparent_index = self.buffer[3]self.state = "结束符检测"self.buffer.clear()elif self.state == "图像标识符":self.buffer.append(byte)if self.state_child == "开始":if len(self.buffer) == 9:self.image_offset_x = struct.unpack('<H', self.buffer[0:2])[0]self.image_offset_y = struct.unpack('<H', self.buffer[2:4])[0]self.image_width = struct.unpack('<H', self.buffer[4:6])[0]self.image_length = struct.unpack('<H', self.buffer[6:8])[0]sign = self.buffer[8]"""- 第 0-2 位:局部颜色表大小(0 = 无局部颜色表)- 第 3-4 位:保留位(0)- 第 5 位:排序标志(0 = 未排序)- 第 6 位:交织标志(0 = 非交织)- 第 7 位:局部颜色表标志(0 = 无局部颜色表)"""self.local_color_sign_number = (sign & 0b00000111)self.local_color_sign_retain = (sign & 0b00011000)self.image_local_color_sign_sort = (sign & 0b00100000)self.local_color_sign_interwoven = (sign & 0b01000000)self.local_color_sign_has_color_table = (sign & 0b10000000)# 检测是否存在局部颜色表格if self.local_color_sign_has_color_table == 128:self.local_color_size = 2 ** (self.local_color_sign_number + 1) * 3self.state_child = "获取局部颜色表"self.buffer.clear()else:self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "获取局部颜色表":if len(self.buffer) == self.local_color_size:# 获取局部颜色表放入局部颜色中self.local_color_one = [(self.buffer[i], self.buffer[i + 1], self.buffer[i + 2]) for i inrange(0, len(self.buffer), 3)]self.local_color_all.append(self.local_color_one)self.state_child = "解析图像"self.buffer.clear()elif self.state_child == "解析图像":if len(self.buffer) == 2:self.image_compressed_bit = self.buffer[0]self.image_block_size = self.buffer[1]self.state_child = "获取数据"self.buffer.clear()elif self.state_child == "获取数据":if len(self.buffer) == self.image_block_size:self.image_bytes_data += bytes(self.buffer) # 转换字节而非对象self.state_child = "检测获取结束"self.buffer.clear()elif self.state_child == "检测获取结束":if self.buffer[0] == 0x00:image_data = self.lzw_decode(self.image_bytes_data)if self.local_color_one: # 如果局部存在 则按照局部生成 如果局部不存在则按照全局生成self.image_data.append([self.local_color_one[image_data[i]] for i in range(len(image_data))])else:self.image_data.append([self.global_color[image_data[i]] for i in range(len(image_data))])self.state = "区块检测"self.state_child = "开始"# 重置数据self.image_bytes_data = b""self.local_color_one.clear()self.buffer.clear()else:self.image_block_size = self.buffer[0]self.state_child = "获取数据"self.buffer.clear()# 解码def lzw_decode(self, compressed):# 初始化字典dictionary = {i: bytes([i]) for i in range(256)}next_code = 258result = bytearray()# 处理压缩数据buffer = 0bits_in_buffer = 0code_size = 9prev_code = Nonefor byte in compressed:buffer |= byte << bits_in_bufferbits_in_buffer += 8while bits_in_buffer >= code_size:code = buffer & ((1 << code_size) - 1)buffer >>= code_sizebits_in_buffer -= code_sizeif code == 256: # 清除码dictionary = {i: bytes([i]) for i in range(256)}next_code = 258code_size = 9prev_code = Nonecontinueelif code == 257: # 结束码return bytes(result)if prev_code is None:result.extend(dictionary[code])prev_code = codecontinueif 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] = entrynext_code += 1prev_code = codeif next_code >= (1 << code_size) and code_size < 12:code_size += 1return bytes(result)passdecoder = GIFDecode()
for byte in data:decoder.feed(byte)print(decoder.version)
print(decoder.image_data)
print(decoder)