GIF 结构检查器
本工具像解码器那样逐块遍历 GIF,从 13 字节的文件头一直走到 0x3B 结尾标记,中间不靠猜测跳过任何字节。文件头给出版本(GIF87a 完全没有扩展块,能做动画的是 GIF89a)、各帧合成所用的逻辑屏幕、背景色索引、三十年来没有任何浏览器理会的像素宽高比字节,以及是否存在全局调色板、有多少项、占几位。随后列出每一帧,并附上决定播放效果的两个值:延迟和处置方式。GIF 显得慢,原因通常就是延迟:它以百分之一秒为单位存储,而浏览器会把大约 20 毫秒以下的值悄悄换成 100 毫秒,于是按每帧 5 毫秒制作的文件比作者设想的慢了五倍。因此这里同时给出两个总时长:延迟相加的结果,以及你实际会看到的长度。处置方式则解释拖影和闪烁:恢复为背景会在下一帧之前清掉本帧矩形,恢复到上一帧会把画布回退,而保留不动正是让优化器只存储变化像素的前提。这种优化在这里同样看得见:小于逻辑屏幕的帧会被标出,并显示它的位置、是否自带局部调色板、是否隔行、以及透明索引。由于子块链是被精确跟随而不是靠扫描找到的,每帧的字节开销是准确值,表格还会按开销排序,让把文件撑到 20 MB 的那一帧排在最上面。剩余字节被拆分为调色板、LZW 图像数据和分块开销,每个扩展块(图形控制、注释、纯文本、应用程序,包括 NETSCAPE 循环计数器和无法识别的块)都会连同偏移、大小和文本一起列出。它不做的事情是解码像素:LZW 数据只被测量,从不解压,所以关于色彩还原、抖动或帧间相似度它给不出任何结论。被截断的文件,或者块长度越过文件末尾的文件,会以带名字的错误被拒绝,而不是读一半就算数——因为从那个位置往后,任何偏移都只是猜测。
使用方法
- 把 .gif 拖到方框里,或点击选择文件。字节在页面内读取,不会上传。
- 先看概览:文件头版本、逻辑屏幕、帧数、浏览器实际播放的时长以及循环方式。
- 如果出现播放警告,说明帧延迟低于浏览器下限。把延迟提高到 20 毫秒以上,否则文件永远比标称的慢。
- 体积问题去看帧表:字节列和进度条显示哪些帧最贵,尺寸旁带 * 表示该帧只重绘画布的一部分。
- 在扩展块列表里检查 NETSCAPE 循环块、编码器留下的注释,以及任何你没预料到的应用程序块。
常见问题
- 为什么我的 GIF 播放得比设定的延迟还慢?
- 因为浏览器拒绝极短的延迟。延迟存放在每帧的图形控制扩展里,单位是百分之一秒,所以非零的最小值是 10 毫秒;但 Blink 和 WebKit 会把低于 11 毫秒的值换成 100 毫秒,Gecko 则是低于 20 毫秒时这么做。于是按“50 fps”导出的文件实际只有 10 fps,慢了十倍,而且没有任何标志能绕过它:这个限制源自当年满页都是用作占位的一帧 GIF,如今所有引擎仍在执行。本工具同时给出延迟之和与浏览器实际耗时,让你分清是被下限拉长的文件还是本来就长的文件。如果确实需要快速动画,答案是视频编码而不是 GIF。
- 循环次数到底表示什么?
- 循环根本不在 GIF 规范里。它是网景公司 1995 年加入的应用程序扩展:11 字节标识 NETSCAPE2.0,后面跟一个含 16 位计数的子块。0 表示无限循环,几乎所有编码器都写这个值。非零值是首次播放之后额外播放的次数,所以 1 在 Chrome 和 Firefox 中会播两遍,不过少数老式阅读器把它当作总次数。如果这个块完全缺失,动画只播一次就停在最后一帧——文件经过会丢弃未知扩展的工具处理后,常常出现这种意外。
- 为什么我的 GIF 这么大?
- 几乎总是因为每一帧都保存整个画面,而不是只保存变化的部分。这里的字节列把一帧的全部开销都算在它头上:图形控制扩展、图像描述符、局部调色板和 LZW 数据,所以排名是精确值而非估算。主导因素有三个:整帧重写,即每帧都和逻辑屏幕一样大;局部调色板,256 项就是 768 字节,六十帧在放入一个像素之前已经是 45 KB;以及 LZW 本身,它对照片和抖动图像表现很差,压完可能比原始索引还大。字节明细会把这三项分开列出。
- 什么是处置方式?为什么画面会留下残影?
- GIF 的每一帧都画在前一帧留下的画布上,处置方式规定在绘制下一帧之前如何处理当前帧。0(未指定)和 1(不处置)会保留画面,这正是差分帧成立的前提——只需存储变化的像素。2 会把该帧矩形清回背景,当透明像素不能层层堆叠时就需要它,但每次都清空整屏就会看到闪烁。3 恢复到该帧绘制前的画面,代价高且各实现并不一致。拖尾和残影几乎总是因为该用 2 的地方用了 1,或者把透明像素叠在从未清除过的画面上。
- 为什么透明边缘看起来是锯齿?
- GIF 的透明是一个调色板索引,而不是 alpha 通道。一帧可以指定某一项为透明,合成时带该索引的像素会被跳过;在完全不透明和完全透明之间没有中间地带。因此 PNG 那种抗锯齿边缘无处安放:半透明像素要么与导出工具假定的背景混合,留下常见的白边或灰边,要么被压成透明索引,于是出现阶梯。它还要占掉一种颜色:带透明的图像可用的是 255 项而不是 256 项。另外透明索引是逐帧的,解码器只会报告当前帧的那一个。
- GIF87a 和 GIF89a 有什么区别?隔行还有用吗?
- GIF87a 是 1987 年的原始格式,完全没有扩展块,也就意味着没有延迟、没有透明、没有循环计数、没有注释。GIF89a 增加了扩展机制,人们心目中 GIF 的种种特性都在其中。两者今天都能被普遍读取,当不需要 89a 专有功能时,编码器写成 87a 是合理的——单张静态图通常就是这种情况。隔行是另一个逐帧的标志:行数据分四遍存储,让只下载了一部分的图像先显示出整幅画面的粗略版本。在现代网络上它没有好处,而且打断了相邻行之间的相关性,反而让 LZW 数据略微变大,所以出现隔行帧多半说明文件经过了某个老工具。
相关工具
WebAssembly 模块检查器
在浏览器中解析 .wasm 文件:各段大小、带完整类型的导入与导出、内存页数、start 函数,以及 name 与 producers 自定义段。
WebP 文件检查器
在浏览器里逐块查看 .webp 的 RIFF 结构:有损、无损还是扩展,画布与帧尺寸是否一致,动画时长、透明通道、ICC、Exif 与 XMP。
MP4 / ISO 基础媒体文件检查器
在浏览器里打开 MP4、M4A、M4V 或 MOV:品牌、快速启动判断、完整 box 树、每条轨道的编解码器与时长,以及 iTunes 标签。
EditorConfig 测试器
粘贴 .editorconfig 和一组文件路径,查看每个文件最终解析出的属性,以及每个值由哪一小节、哪一行决定。
gzip(.gz)结构检查器
逐字段读取 .gz 头部,遍历每个成员,并在浏览器中重新计算 CRC32 和真实大小来校验尾部记录。
JPEG 结构检查器
打开 .jpg 查看它的标记段:基线还是渐进、色度抽样、估算质量,以及文件的字节到底花在了哪里。