尽管 GIF(图形交换格式)在现代 Web 上不是非常有用,但它为图像编码的核心概念提供了可靠的介绍。
可以将 GIF 视为图片数据的封装容器。它具有一种视口,称为“逻辑屏幕”,图片数据的各个帧绘制到该视口上,这有点像 Photoshop 文档中的图层。这就是 GIF 支持其类似翻页书的动画的方式:将单个帧绘制到逻辑屏幕上,然后被另一个帧替换,再被另一个帧替换。当然,当我们处理静态 GIF(由绘制到逻辑屏幕上的单个帧组成)时,这种区别并不重要。
GIF 使用无损数据压缩方法,如果您好奇,这是一种 “Lempel-Ziv-Welch” 算法的变体。我们无需深入了解该算法的工作原理的更精细细节,但在较高层面上:它的工作原理有点像“丑化”JavaScript,其中整个文件中重复的字符串字符都保存到某种内部词典中,以便可以引用它们,而不是每次出现时都重复。
诚然,该算法并不像数字填色那样简单。它再次遍历生成的颜色代码表,以查找重复的像素颜色序列,并创建第二个可引用的代码表。但是,图像数据在任何时候都不会丢失,只是以一种可以读取而不会从根本上改变它的方式进行排序和重组。
虽然 GIF 在技术上使用无损压缩,但它确实有一个严重影响图像质量的主要限制:除非图像已使用 256 种或更少的颜色,否则将图像另存为 GIF 始终会导致保真度降低。
绘制到 GIF 逻辑屏幕的每个帧最多只能包含 256 种颜色。GIF 还支持“索引透明度”,其中透明像素将引用颜色表中透明“颜色”的索引。
将一系列值缩小为更小的、近似的输出值集的过程称为量化,这是一个您在学习图像编码时会经常看到的术语。这种调色板量化的结果通常很明显
为了更好地理解这个过程,请回想一下您能够根据我的描述重新创建的栅格图片网格。
这次,为原始图片添加更多细节:再添加几个像素,其中一个像素是稍深一点的蓝色阴影
如果没有压缩(可以这么说),您可以将此网格描述为
第一行,第一列是 #0000FF。第一行,第二列是 #0000FF。第一行,第三列是 #0000FF。第一行,第四列是 #FF0000。第二行,第一列是 #0000FF。第二行,第二列是 #000085。第二行,第三列是 #0000FF。第二行,第四列是 #FF0000。
使用类似于 GIF 的无损数据压缩和颜色索引的技术,您可以将其描述为
A:#0000FF,B:#FF0000,C:#000085。第一行,第一列到第三列为 A。第一行,第四列为 B。第二行,第一列为 A。第二行,第二列为 C。第二行,第三列为 A。第二行,第四列为 B。
这设法在几个地方(“第一列到第三列为...”)浓缩了逐像素描述,并通过预先在某种词典中定义重复颜色来节省了一些字符。视觉保真度没有变化。信息已被压缩,没有任何损失。
但是,正如您所见,单个深蓝色像素对我们编码的大小产生了过大的影响。如果我将自己限制为量化的调色板,则可以进一步减小它
A:#0000FF,B:#FF0000。第一行,第一列到第三列为 A。第一行,第四列为 B。第二行,第一列到第三列为 A。第二行,第四列为 B。
这些节省的字节带来的不幸最终结果是,您丢失了像素完美度。
当然,作为渲染引擎的您并不知道这一点,更深蓝色像素的细节已从我编码源图像的方式中遗漏了。您已完全按照我的编码方式渲染了图像,这基于我们对我们手头颜色的共同理解。
现在,在这个夸张的示例中,将三种颜色减少到两种会在质量上产生明显的差异。在更大、更详细的图像中,效果可能不会那么明显,但仍然是可见的。
当编码为 GIF 时,像阴影这样的细微渐变会变得斑驳,单个像素会从周围环境中突出出来
在实践中,无损压缩和调色板量化的结合意味着 GIF 在现代 Web 开发中不是很有用。无损压缩不足以减小文件大小,而调色板的减少意味着质量的明显降低。
最终,GIF 仅适用于编码已使用有限调色板、硬边而不是抗锯齿以及纯色而不是渐变的简单图像,对于所有这些用例,其他格式都能更好地满足需求。对于栅格图片,功能更丰富且文件更小的 PNG 通常是更好的选择,尽管对于图标或线条艺术等用例(矢量图在这些用例中表现出色),就文件大小和视觉保真度而言,两者都远不如 SVG。GIF 最常见的现代用例是动画,但有更高效且更易于访问的现代视频格式可以满足该目的。