当前位置: 首页 > news >正文

Python |GIF 解析与构建(2):状态机解析

Python |GIF 解析与构建(2):状态机解析

目录

Python |GIF 解析与构建(2):状态机解析

引言

一、状态机概述

状态机的优势与改进方向

总结


引言

在《Python |GIF 解析与构建(1):初步解析》中,我们初步进行解析gif文件。

本文将深入探讨如何使用 ** 状态机(State Machine)** 实现 GIF 文件的解析,通过分阶段处理数据流,逐步提取关键信息(如版本、颜色表、图像数据等)。状态机的优势在于将复杂的解析逻辑拆解为多个可管理的状态,每个状态专注于处理特定的数据流片段,从而提高代码的可读性和可维护性。

一、状态机概述

GIF 文件由一系列区块(Block)组成,每个区块包含特定类型的数据(如文件头、颜色表、图像数据、扩展指令等)。状态机的核心思想是:根据当前解析进度,将程序划分为多个状态(State),每个状态负责解析某一类区块或数据片段,并根据输入数据决定下一步跳转的状态。

GIF 解析的主要状态包括

  1. 开头(Header):验证 GIF 文件签名(GIF87aGIF89a)。
  2. 逻辑屏幕标识符(Logical Screen Descriptor):解析画布尺寸、颜色表标志等基础信息。
  3. 全局颜色表(Global Color Table):提取全局调色板(若存在)。
  4. 区块检测(Block Detection):识别后续区块类型(如图像标识符、扩展块等)。
  5. 扩展块处理(Extension Blocks):解析图形控制扩展(如动画延迟)、应用程序扩展(如循环参数)。
  6. 图像标识符(Image Descriptor):解析图像位置、尺寸、局部颜色表(若存在)及压缩数据。
  7. 结束符检测(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)

http://www.xdnf.cn/news/618319.html

相关文章:

  • 2000-2023年各地级市进出口总额/地级市对外经济贸易数据
  • queue和priority_queue及其函数
  • ld: cpu type/subtype in slice (arm64e.old) does not match fat header (arm64e)
  • mysql连接池druid监控配置
  • 2025年工会考试题库及答案
  • MyBatis 中 parameterType 属性
  • AutoCAD Electrical 自定义多极元件
  • 反本能---如何对抗你的习以为常
  • 二分算法(灵神边界处理复习)
  • 电子电路:能认为电抗也是在做功吗?
  • 软件测试(4) 白盒测试
  • 归一化与标准化
  • 频率分布直方图
  • halcon初始
  • 深度剖析并发I/O模型select、poll、epoll与IOCP核心机制
  • 计算机组成原理-基本运算部件定点数的运算
  • 【安全攻防与漏洞​】​​Heartbleed漏洞复现与修复
  • 【JS】vue3具名导出与默认导出
  • [Asp.Net]GridView导出Excel长数字显示成科学计数
  • Spring Boot 项目多数据源配置【dynamic datasource】
  • C++进阶--c++11(02)
  • 【算法】: 前缀和算法(利用o(1)的时间复杂度快速求区间和)
  • 全球复合铁路枕木市场深度分析:技术革新与区域增长潜力(2024-2031)
  • IIS部署微信支付模块问题
  • 欧拉公式的历史脉络、数学证明和现代意义
  • 信息学奥赛及各种程序设计竞赛中常见的名词解释
  • Android四大组件学习总结
  • PyQt学习系列07-数据库操作与ORM集成
  • JavaMail的使用
  • 重读《人件》Peopleware -(12-1)Ⅱ 办公环境 Ⅴ 大脑时间与身体时间(上)