在 PNG、JPEG、WebP 和 AVIF 之间做选择
照片用 JPEG、WebP 或 AVIF。截图、标志、图标、示意图,以及任何有大片纯色或清晰文字的东西,用 PNG 或者 WebP 的无损模式。如果你需要透明度,那就完全排除了 JPEG。如果你今天要为网页选格式,而且能同时提供多种格式,那就 AVIF 优先、WebP 作为退路,再为特别老旧的环境保留原始的 JPEG 或 PNG。
整个决策就是这些。下面解释为什么,好让你知道什么时候这条经验法则不适用。
每种格式各自真正擅长什么
| 格式 | 压缩方式 | 透明度 | 动画 | 最适合 | 最不适合 |
|---|---|---|---|---|---|
| PNG | 无损 | 支持,完整 alpha | 不支持 | 截图、标志、图标、线稿,以及任何带文字的图 | 照片,文件会变得非常大 |
| JPEG | 只有有损 | 不支持 | 不支持 | 照片,以及需要通用支持的场合 | 锐利边缘、纯色块、文字,以及任何你还要再编辑的图 |
| WebP | 有损或无损 | 支持,两种模式都支持 | 支持 | 在网页上通用地替代 PNG 和 JPEG | 大约 2020 年以前的软件 |
| AVIF | 有损或无损 | 支持 | 支持 | 照片的最小体积、广色域和高动态范围 | 快速编码,以及打不开它的老旧软件 |
无损意味着解码出来的图像与输入的逐位完全相同。有损意味着编码器永久性地丢掉一部分信息以换取更小的文件,你拿回来的是一个近似值。
PNG 是无损的,而且永远都会是。它的压缩靠的是根据相邻像素预测每个像素,再对差值做 deflate 压缩,当大片区域相同或重复时效果极佳,而当每个像素都略有不同(比如照片)时效果很差。同一张照片存成 PNG,往往比一份质量不错的 JPEG 大五到十倍,而且看不出任何好处。
JPEG 是有损的,也没有 alpha 通道,所以它根本存不了透明度。它把图像切成块,把每一块转换成频域信息,再丢掉你的眼睛最不敏感的那些细节。这在连续色调的照片上效果极好,在硬边缘上则很差,那正是存成 JPEG 的文字周围出现淡淡光晕和蚊状噪声的原因。
WebP 和 AVIF 都是从视频编解码器派生出来的现代容器格式。两者都支持有损和无损,都支持完整的 alpha 透明度,并且当前所有主流浏览器都支持。AVIF 的压缩通常比 WebP 更狠,尤其是在低质量设置下,它还能处理广色域和更高的位深度。代价是编码时间,可能明显更慢,以及在浏览器之外的支持面稍窄一些,尤其是老一些的桌面应用和图像工具。
透明度
只有 PNG、WebP 和 AVIF 能存储真正的 alpha 通道,也就是每个像素都带着自己的不透明度值。这正是标志能干净地摆在任何背景色上的原因。
如果你把一张透明的图转成 JPEG,那份透明度总得有个去处。多数工具会把它压平到白色上,有些压到黑色上,而你只有在图片出现在彩色背景上、周围顶着一个白框时才会发现是哪一种。如果你知道最终的背景色,就有意地压平到那个颜色上,而不是让转换器去猜。
什么时候有损没问题,什么时候不行
对于将以实际展示尺寸交付的照片类内容,有损没有问题。在合理的质量设置下,损失确实很难看出来。
以下情况用有损是个坏主意:
- 图像里有文字、细线条或大片纯色区域。压缩瑕疵恰恰会聚集在硬边缘上。
- 这个文件是你之后还要编辑并重新导出的工作母版。
- 这张图之后会被放大,或者被大幅度调色,那会把编码器做过的一切放大。
- 精确的像素值很重要,比如科研、医学或司法取证工作,或者一张有人要从上面读数字的截图。
为什么反复保存 JPEG 会让它越来越差
每次 JPEG 被解码再保存一遍,编码器都会对已经量化过的数据再量化一次,舍入误差不断累积。问题不在于画质相对原图被削减了两次;而在于第一轮引入的瑕疵,现在被当成了真实的图像细节,又被近似了一遍。
用更高的质量设置去保存也撤销不了它。第一次保存时被去掉的细节已经没了。从一个质量 60 的文件用质量 95 重新编码,只会得到一个更大的文件,忠实地保留着质量 60 的瑕疵。如果你要反复编辑一张图,就把母版保存为无损格式,最后再导出 JPEG、WebP 或 AVIF。
体积的大致预期
以下是经验法则,不是实测数据,真实数字会随图像内容剧烈变化。对于一张典型照片,在相同的感知质量下,可以把 JPEG 当作基准线,WebP 大约小四分之一左右,AVIF 还要更小,而且压得越狠差距越大。对于纯色图形,无损 WebP 通常胜过 PNG,有时优势相当可观。噪点和细密纹理会让所有有损格式都变大,所以一张带颗粒的照片永远压不出干净的影棚照那样的体积。
先缩放,再压缩
最大的一次性收益几乎从来不是格式,而是尺寸。一张以 4000 像素宽提供、却显示在 800 像素栏位里的图,携带的像素数据大约是它所需的二十五倍,而没有哪个编码器能弥补这一点。先缩放到它大致会被展示的尺寸(如果你面向高密度屏幕,就取两倍),然后再选格式,最后再调质量参数,一直调到你刚刚能看出差别,再退回一档。