abox.tools

语言: 简体中文

一个 GIF 里到底装着什么

GIF 是一摞长方形,每个都带着一只计时器和一张调色板,而人们对这个格式的抱怨,几乎都出自这三样里的某一样。下面讲每个部分各做什么,以及怎么弄清楚你的文件把体积花在了哪一样上。

打开GIF 分析 帧、延时、调色板,以及每个字节的去向。

以 Markdown 查看

简短的回答

一个 GIF 是一块画布、一串往上画的长方形,外加一张调色板,说明那些长方形里的数字各代表什么颜色。每个长方形带着三样东西:它该停留多久、之后该拿它怎么办,以及可有可无的一张自己的调色板。

人们觉得这个格式奇怪的地方,几乎都出自那串长方形。你的 GIF 大得离谱,要么是那些长方形每次都是整块画布,要么是里面塞了三百张调色板。它播得太慢,是因为延时低到了任何浏览器都不肯往下走的那条地板以下。它拖出尾巴,那是那个叫处置的字段。

想知道某个具体文件属于哪一种,打开 GIF 分析把它拖进去。这一页剩下的部分,讲的是那些数字都是什么意思。

一个 GIF 的摘要卡片:版本、画布尺寸、文件大小、帧数、循环次数和用了多少种颜色。
播放器从不告诉你的一切,全从一个文件里读出来。

帧是长方形,不是画

只见过 GIF 播出来的人,在这儿最容易吃惊。一帧并不是那一刻动画的画面。它是一个长方形,有自己的位置和自己的大小,画在前面几帧留下的东西之上。

那个长方形可以是整块画布,而在做得糟的文件里它每次都是。但 GIF 是允许只存下自上一帧起变过的那部分的——而在画面大半不动的地方,这就是一个 12 MB 的文件和一个 900 KB 的文件之间的差别。一段几乎静止的窗口录屏能做得很小,而同一段录屏从一个马虎的转换器里出来就不小,原因也在这儿。

光看动画,你分不出自己手上是哪一种,两者看起来一模一样。唯一的办法是看每一帧存了什么,而分析器专为此备了一个视图:把它切到每帧实际存的东西,你要么看到透明底上一排小形状,说明编码器干了它的活;要么一遍又一遍看到整张画面,说明它没干。

这个格式里任何地方都没有运动补偿。不会有东西被存成「和上次一样,只是往左挪了四个像素」,视频编码器才那么干。变化区域这个招数是 GIF 仅有的一点省法,而它非常值钱。

延时,以及每个浏览器都强推的那条地板

每一帧都存着自己要停多久,单位是百分之一秒。这个格式只有这一种单位,所以一个文件能要的最快是 0.01 秒——每秒一百帧——最长约 655 秒。

它不会得到每秒一百帧。每个浏览器都把低于 0.02 秒的延时抬到 0.10。这条规矩 1996 年写进了 Netscape Navigator,为的是那个年代旋转的地球仪和会动的「施工中」牌子,之后每个浏览器都照抄了下来。从来没人删掉它,也不会有人删。

所以一个每帧都写着 0.01 秒的 GIF,播出来是每秒十帧,不是一百帧。它比造它的那个程序打算的慢十倍,而文件对此毫无提示:里面的延时正是当初要求的值。这是这个格式里最常见的意外,也是分析器要报两个时长的原因——文件说的那个,和浏览器真正会照办的那个。

无论文件是在哪儿造的,解法都是写 0.02 而不是 0.01。那是每秒 50 帧,也是真正的上限,比任何用途需要的都快。实际上 0.05 秒——每秒二十帧——差不多就是值得去要的最快了。

延时还能告诉你另一件事。要是它们全都一样,这个文件是由一组固定帧率的帧做出来的。要是它们参差不齐——这儿 0.04,那儿 0.11——那是某个东西转了一段视频、丢掉了一些帧,再把邻居拉长去补上窟窿。而要是最后一帧比其余的长得多,那是有意为之:让动画在循环回去之前停一下,就是这么做的。

处置:决定它会不会拖尾巴的字段

每一帧都写明自己的时间到了之后,屏幕上该留下什么。可能的答案有四个,值得知道,因为动画看起来不对的四种方式里,有三种就是这个字段填错了。

有一处规范和现实分了家。「清回背景」指的是文件头里写着的那个背景色,而每个浏览器都无视它,改成清成透明。这样已经二十五年了。一个指望那个背景色会出现的文件,在做它的人、用做它的那个软件看来是对的,在别处一律是错的。

调色板,以及它们要的 768 字节

GIF 的一个像素不是颜色。它是一个数字,指向一张最多 256 色的表,每种颜色占三个字节。所以一张满的表是 768 字节,而一个文件可以让所有帧共用一张,也可以一帧一张,或者两者都有。

两种安排都合法,只是换来的东西不同:

还有一笔更安静的开销。调色板的长度必须是 2 的幂,所以用九种颜色的帧照样拿到十六格的表,用 130 种的照样拿到 256 格。一定程度的往上凑是躲不掉的。但要是一个文件的表里声明了五千种它的像素从没引用过的颜色,那就是另一回事了:那是为另一张画配的调色板,而最终进到帧里的并不是那张画。分析器会把没用上的格子标出来,让这个形状一眼可见。

字节实际去了哪里

GIF 的每个字节都落在为数不多的几个地方,在断定一个文件太大之前,值得知道这几个地方是什么。

要看这张表而不是猜,是因为答案因文件而异,而解法直接从答案里长出来。一个 95 % 都是压缩像素的文件,就是画面本身多,只有减帧、缩小尺寸或者减颜色才有用。一个 30 % 是调色板、或者 40 % 是 XMP 的文件,问题便宜得多。

一条按字节去向拆解 GIF 的条形图,每一帧一行,给出各自的大小和在文件中的占比。
字节到底去了哪里,一帧一帧地看。太大的 GIF 几乎总是因为一个看了这张图就明白的原因才大的。

循环并不属于这个格式

GIF 规范里没有任何字段说动画会重复。循环来自 Netscape 在 1995 年发明的一个块——一个里面写着 NETSCAPE2.0 的「应用扩展」——所有人还是都实现了它,如今互联网上每一个动图 GIF 里都有它。

这也意味着,没有这个块的文件在每个浏览器里都是恰好播一遍就停,在做它的人看来像是坏了。要是一段动画只走一遍,就是这个块不见了;这是最值得先查的几件事之一,而且在任何看图程序里都看不见。

这个块也能写一个次数——播五遍然后停。零表示永远,而几乎所有文件写的都是零。

GIF 还能捎带的别的东西

有三种块不装画面,而且所有看图程序都跳过:

这三样在把文件发出去之前都值得知道:它们是一个 GIF 里可能透露你的部分,而且除非有东西专门把它们剥掉,否则每一次拷贝、每一次重新上传,它们都活着。

读一个损坏的文件

GIF 会被截断——中途停掉的下载、从快坏的硬盘上救回来的文件、某个程序只写了一半的东西。因为这个格式是一串块,而不是一整个带索引的结构,被截断的 GIF 通常仍然能读到它断掉的那一点为止:断口之前的每一帧都是完整无损的。

这值得知道,因为大多数软件会干脆拒收这个文件。一个尽量往下读、并且告诉你在哪儿停住的分析器,至少能让你知道有多少活了下来,以及丢掉的是一帧还是最后两百帧。

反过来的毛病也存在:坐在文件结束标记之后的那些字节。每个解码器都在那个标记处停下,所以它们从不会被读到、也不会被画出来,而它们通常是某次出错时被接在第一个文件后面的第二个文件。它们是纯粹的重量,切掉不损失任何东西。

这些都不需要上传

读一个 GIF 的结构不是什么费劲的活——走一遍块的清单,再加一个小小的解压器——而且从来没有任何技术上的理由,非要把文件送到服务器上去做它。这里的 GIF 分析把整件事都放在页面里做完:走块、LZW、画到屏幕上的帧,还有那份字节账。

这件事在这儿比在多数活儿上更要紧,因为人们最想拆开看的文件,往往正是他们最拿不准要不要拿出去的那些——救回来的东西、别人发来的东西、里面有一段自己还没读过的注释的东西。关于上传文件的那篇更长的说理,在这儿和在本站任何地方一样成立。