为什么给 GIF 去水印比给照片去水印难
照片是一张图,GIF 是一条时间线。一个 GIF 去水印工具必须回答三个静态图工具永远不会遇到的问题:怎么解码帧、怎么让修复在各帧之间保持一致、怎么在重新编码时不把颜色搞坏。
解码:帧并不是各自独立的图片
很多动图只存了与上一帧相比发生变化的那块像素,外加一条处置指令(disposal),用来说明之后这块区域该怎么处理。要正确修复第 12 帧,得先还原第 12 帧实际长什么样,也就是从第 0 帧开始把处置链重放一遍。这个工具用一个流式合成器来做这件事,最多只同时持有四张画布,所以 60 帧的 GIF 不会膨胀成 60 张全尺寸位图。
修复:一张遮罩,多个帧
每一帧都单独画一次选区太折磨人了,所以你只在第一帧标记,这张遮罩会被复用。这也解释了为什么它最适合位置固定的叠加层 —— 角落的 logo、日期戳、字幕条。会横跨画面滑动的叠加层,是一张遮罩描述不了的。
顺便说一句,对每一帧都跑模型也很浪费。修某一帧之前,会先把该帧选区内的区域和上一帧的同一区域对比,几乎一致就直接复用上一次结果、跳过模型。一段带固定水印的录屏里,相当一部分帧根本不会碰到模型。
重新编码:为什么必须共用一张调色板
GIF 只能放 256 种颜色。如果逐帧独立量化,同一种颜色在不同帧会被映射到不同的调色板索引,播放时看起来就是颜色闪烁。所以这里改成:在整段动画里采样最多八帧拼成缩略图,从这里算出一张调色板,所有帧都按它来映射。透明色会在这张调色板里占一个专用槽位,而不是抢走某个真实颜色的位置。
正是这条流水线把一个正经的 GIF 去水印工具和「碰巧能导入 GIF 的图片编辑器」区分开:动图被当成一串互相依赖的帧,而不是一张洗完就压平导出的一张图。
