跳到主要内容

编码简史与图形编码

省、稳、验三种动机与图形编码密度

周一 8月 31 2026
9012 字 · 31 分钟

编码简史与图形编码

编码的本质是换一套符号来书写信息,但一条编码史里藏着三种截然不同的动机:省——用更短的符号表达同样的内容;稳——让信息在噪声信道里活下来;验——给数据造一个能暴露篡改的指纹。三条线分别通向压缩、纠错码与哈希函数,而日常语境里「编码、加密、哈希」的混用,恰好说明这条历史线没有被理顺。本文前半从 1838 年的电报线走到现代密码学哈希,后半把镜头转向图像里的编码。

一条编码简史:省、稳、验

不定长编码

高频短码

摩斯电码是人类第一套大规模实用的字符编码,而它最精彩的决策出现在设计初期。

助手维尔(Alfred Vail)统计了英文报纸活字盘里各字母的使用频率,把最短的符号分给最高频的字母。E 是一个点,T 是一个短划,而 Q 要四笔。

英文里 E 出现频率约 12%,T 约 9%。让它们各省下两三笔,整篇电报的费用就省下一大截。

这是「按使用频率分配码长」思想的第一次落地,比香农的信息论早了一百多年。压缩的全部直觉,最初是一笔电报费账。

不定长编码立刻带来一个新问题:怎么知道一个字符在哪里结束?

等长的编码按固定间隔切分即可,长短不一时必须另加约定。

摩斯电码的答案是用时间当分隔符。点记 1 个时间单位,划记 3 个,字符内点划间隔 1 个单位,字符之间停 3 个单位,单词之间停 7 个单位。

敲电码本质上是把一维的字符流编码成了「信号与静默」的交替序列。分隔符本身也是编码的一部分。

在摩斯电码互转里把一句话转成电码,观察字符之间的间隔约定,能直观看到「不定长必然引入分隔代价」这个规律。

电报员们还在这套编码上发展出第二层复用:用高频组合造缩写。

求救信号 SOS 的三短三长三短并不是任何单词的缩写,它被选中纯粹因为点划交替的图案极其独特、误读率最低。且作为整体信号发送时字符间停顿被刻意取消。

这是「把最容易识别的模式留给最关键的语义」的经典设计。

类似的还有 Q 开头的缩写电码、报务员之间的习惯简语。当码长本身有代价时,「常用语义整体再编码」是必然的演化方向。这个思路一百五十年后以另一种面目出现在 HTTP 头部压缩里。

哈夫曼

分隔符要占带宽,有没有不靠分隔符的不定长编码?

有。1952 年哈夫曼(David Huffman)在 MIT 的一门课程作业里给出的前缀码:规定任何字符的编码都不是另一个字符编码的前缀。

这样一来,从编码流的开头往下降,每读到一个完整码字就能立即切开。解码无歧义,分隔符彻底免掉。

前缀性质可以翻译成一个优雅的几何条件(Kraft 不等式):设码长为 l1,l2,⋯l_1, l_2, \cdots,前缀码存在当且仅当

∑i2−li≤1\sum_i 2^{-l_i} \le 1

直觉是把二叉树当作编码空间:每个码字占据树上一片叶子,前缀互斥等价于「码字不占祖先的位置」,所有叶子瓜分总长度 1 的概率区间。

哈夫曼给出的构造算法同样干净。统计每个符号的频率,放入优先队列。每次取出频率最小的两个,合并成一个新节点(两者编码由此各多一位),新节点按合并频率放回队列。重复到只剩一个根。

可以证明这个贪心构造在「码长必须是整数比特」的前提下最优。没有任何其他前缀码的平均码长更短。

而最优的平均码长也有下界,就是 1948 年香农定义的信息熵:

H=−∑ipilog⁡2piH = -\sum_i p_i \log_2 p_i

对英文文本,逐字符熵约 4.1 比特上下,而 ASCII 用 8 位。哈夫曼能把英语文本压到平均 4.5 比特每字符左右,距离理论下界一步之遥。

压缩这件事到这里完成了理论闭环:熵是价格底线,哈夫曼是逼近底线的整数解,剩下的差距(分数比特)由算术编码这类后续技术补齐。

从哈夫曼到 zstd

哈夫曼在今天的压缩软件里仍然无处不在,但已经退居第二线。因为压缩的真正大头被另一类算法接走了。

gzip 的核心是 DEFLATE,两段式架构。先用 LZ77 类算法在数据里找重复片段,把「重复出现」替换成「向前引用」(距离加长度),这一步消灭的是结构冗余。剩下的引用符号与字面字节再交给哈夫曼编码,消灭频率冗余。

现代的 zstd、brotli 沿用同一分层,只是把引用找得更准(更大窗口)、把熵编码换得更高(从整数比特的哈夫曼换成逼近分数比特的 ANS)。

可以看出哈夫曼的位置:建模归 LZ,编码归熵。熵编码是所有压缩器的最后一公里,而这一公里至今仍常由七十年前那个课程作业的变体来跑。

定长编码与字节流

电传打字机与字节的胜利

与摩斯的精打细算相反的另一条路线是定长:不管频率高低,所有符号给一样长的码。

1874 年的博多码(Baudot)给每个字母 5 个比特,服务机械电传打字机。1963 年的 ASCII 用 7 个比特,后来又和字节(8 位)捆绑成事实标准。

定长编码浪费带宽,但换来两样值钱的东西。寻址简单(第 n 个字符在位置 n×Ln \times L,与现代内存分页天然契合)和免分隔。

计算机时代最后选择了定长为主、压缩按需叠加的架构。字符集负责统一书写,压缩交给 gzip 这类独立层。

Base64

到了电子邮件和网页时代,一个新问题出现:很多信道只肯运文本。

早期邮件系统按 7-bit ASCII 设计,高位的字节直接被掐掉。URL 里空格、斜杠都有语法含义。JSON 天然是文本格式。

要让一张图片、一份证书、一段二进制签名通过这些信道,就得把它伪装成纯文本。这就是 Base64 的工作。

算法朴素到极致。把输入按每 3 个字节(24 位)一组切开,重切成 4 个 6 位的块,每个 6 位值查表映射到一个可打印字符(A–Z、a–z、0–9、+、/ 共 64 个)。凑不满 3 字节的尾部补零并用等号填充。

代价是体积膨胀到原来的 4/3。

这里必须钉死一个高频误解:Base64 不是加密。

它没有密钥,任何人拿到字符串都能瞬间还原,映射表是公开的标准。它通常还让数据变长而不是变短。

它的身份是编码。改变书写方式以便通过特定信道,与保密毫无关系。

看到 JWT(JSON Web Token)里那三段「乱码」,或者网页内嵌图片的 data URL,要知道那都是 Base64 编码而非加密处理。

想亲手体会「编码可逆、人人可解」,在编解码工具里做 Base64 与 URL 编解码,把同一段文本编了又解、解了又编,「这不是保密」的体感就建立起来了。

Base64 还有一组实践层面的坑值得记录。

其一是变体分裂。标准字符表里的加号和斜杠在 URL 里各有语法含义,于是有了把两者换成连字符与下划线的 URL 安全变体。JWT 用的正是它。

其二是填充的歧义。= 只是凑齐 4 的倍数的补位,解码时可有可无,导致同一个内容存在「带填充」与「不带填充」两种合法写法,系统对接时对不上往往就卡在这里。

其三是换行。MIME 规范要求 Base64 每 76 个字符折行,从邮件里复制出来的编码带着换行符,直接解码就失败。

三个坑指向同一句教训:拿到一段 Base64,先问是哪一家的 Base64。

UTF-8

Base64 的故事遗留了一个更深的问题:字符编码本身走定长还是变长?

计算机早期各自为政的编码在互联网上撞车之后,统一字符集 Unicode 成为共识,剩下的问题是每个字符占几个字节。

定长的 UTF-32 浪费得离谱(一个英文字母占 4 字节)。UTF-16 折中为 2 字节起步、生僻字符用代理对扩到 4 字节,但 ASCII 兼容性为零,还背着字节序的包袱。

胜出的是 1992 年 Ken Thompson 与 Rob Pike 在一张餐垫纸上设计的 UTF-8。1 到 4 字节的变长编码,首字节的高位模式直接声明「本字符共几字节」,续字节统一以 10 开头。

这套结构送了三份厚礼。

ASCII 是天然的子集。128 以内的字符原字节不动,存量文本无缝升级。

自同步。从字节流的任意位置开始读,靠首字节模式就能找到下一个字符的边界,一个损坏字节不会污染整段。

前缀码的旧智慧。首字节的位模式正是哈夫曼式的变长前缀设计,高频的拉丁字符 1 字节、汉字 3 字节,频率与码长再次挂钩。

今天互联网上超过九成八的页面是 UTF-8。这场胜利常被归功于兼容性,但结构上的自同步与变长平衡,才是它把 UTF-16 逐出网页的根本原因。

校验与纠错

从奇偶校验到汉明码

信息在信道里传输会被噪声打翻比特,存储介质会老化,这是第三种动机稳的来源。

最朴素的手段是奇偶校验。给每个字节补一位,让 1 的总数凑成偶数,任何单比特翻转立刻暴露。代价是只能检错不能纠错,且两位翻转就漏网。

1950 年汉明(Richard Hamming)在贝尔实验室给出的方案把能力推到「自动纠错」。在 7 个比特里安插 3 个校验位,校验位放在 2 的幂次位置(第 1、2、4 位),每个校验位负责一组特定位置。

接收端把各校验方程重算一遍,不符方程的编号按二进制相加,结果就是出错比特的位置。把它取反即完成纠错。

这个(7,4)汉明码能纠正任意单比特错误,后来守护了几十年的 ECC 内存。

纠错码的核心思想值得单独说破:用冗余把合法码字在编码空间里撑开。

定义两个码字的「汉明距离」为不同比特数。若所有码字两两距离至少为 3,那么任何单比特错误落点都离原码字最近、离其他码字至少还有 2。按「最近邻」判决即可恢复。

距离 3 检二纠一,冗余与纠错能力之间的汇率就这样确定。

工程上更常用的是循环冗余校验(CRC)。把数据看作多项式,除以约定的生成多项式取余数作校验码,硬件上就是一排异或门,能以极低成本检出成串的突发错误。以太网帧尾、ZIP 文件里都有它。

校验和的日常形态

这套思想早就渗透进日常生活,只是多数人没认出来。

图书封底的 ISBN 末位是校验位。把前 12 位按 1、3 交替加权求和,补一个使总和被 10 整除的尾数。扫码枪读错一位立刻报警。

身份证号的第 18 位同理,是前 17 位按固定权重加权后对 11 取模算出的校验码。

银行卡号的 Luhn 算法更巧妙。从右往左隔位翻倍、超过 9 就减 9,最后总和被 10 整除。它能抓住几乎所有的「相邻数字抄反」这类高频输入错误,因为翻倍操作对不同位置的错法敏感度不同。

日常见到的「输错一位就提示非法」,背后全是这同一族算法:用一两位的冗余,把绝大多数单点错误挡在入口。

哈希

为什么哈希不属于编码

前三段故事的共同点是可逆:编码是为了换写法,必要时必须能换回来。

哈希函数故意砍掉了这条路。它把任意长度的输入压成固定长度的输出(MD5 是 128 位,SHA-256 是 256 位),而输入空间远大于输出空间,鸽笼原理保证碰撞不可避免。无穷多个输入共享同一个摘要。

既然信息被压缩掉了,「解哈希」在数学上就不存在。

所以哈希的正确身份是数据的指纹。指纹能唯一辨认人,却没法从指纹长回一个人。

什么样的指纹算好指纹?密码学哈希有三个递进的硬指标。

抗原像:给定摘要,找不到对应输入。

抗第二原像:给定输入,找不到另一个同摘要的输入。

抗碰撞:任意找一对同摘要的输入都不可行。

支撑这些性质的是雪崩效应。输入翻转一个比特,输出大约一半的位都要翻转,使得攻击者无法顺着输出反推输入的局部。

这套防线的历史值得记住。MD5 在 2004 年被王小云团队给出了实用的碰撞构造,从此不能用于任何安全场景。SHA-1 在 2017 年被公开碰撞实例击倒。当前的默认选择是 SHA-2 系列(SHA-256)。

在哈希生成器里输入同一段文本,看 MD5 与 SHA-256 各自的输出,再把文本改一个字母重新生成。雪崩效应用眼睛就能确认。

哈希的三个日常战场

第一个战场是完整性校验。下载文件时对照官方公布的 SHA-256,摘要不符即传输损坏或被篡改。这里利用的是抗第二原像,攻击者无法伪造一个同摘要的坏文件。

第二个战场是密码存储。数据库里只存盐加哈希,登录时把输入哈希后比对。

注意两个细节。加盐是为了让相同密码产生不同摘要,破解者无法用预计算表批量命中。而现代做法更进一步,刻意用慢哈希(bcrypt、scrypt、argon2,内置上千次迭代与内存消耗),把暴力枚举的成本抬高几个数量级。

直接用 SHA-256 存密码是常见的事故,因为通用显卡每秒能算上百亿次。

第三个战场是去重与分片。网盘的秒传、Git 的内容寻址存储、区块链的区块链接,都是「摘要即身份」的应用。

布隆过滤器

碰撞在密码学里是敌人,在另一处却是原材料。

1970 年的布隆过滤器把哈希的「多对一」直接用作空间压缩。开一个很长的位数组,插入元素时用 k 个独立哈希函数把对应 k 个位置置 1。查询时看 k 个位是否全为 1。全 1 则「可能在」,有 0 则「绝不在」。

误判只朝一个方向:说不在就一定不在,说在可能误报。

用千分之几的误报率,能把上亿元素的集合压到每元素几个比特。于是一次内存查询就能挡掉绝大多数「肯定不存在」的查找。缓存穿透防护、爬虫去重、存储引擎里「这个键可能在哪个文件」的初筛,全是它的地盘。

设计要点只有一条:位数组长度、元素数量与哈希个数三者的配比决定误报率,配比公式一查就有,但「宁可误报、不可漏报」这个语义前提必须先想清楚。

布隆过滤器是把碰撞从缺陷转化为特性的教科书案例。也再次划清了它与密码学哈希的分工:后者要求碰撞难找,前者根本不在乎碰撞在谁身上。

一张表分清

可逆性密钥信息是否保留典型用途例子
编码可逆无完整保留过信道、标准化书写Base64、URL 编码、摩斯
加密可逆有完整保留(仅密钥持有者可读)保密AES、RSA
哈希不可逆无刻意丢弃校验、指纹、口令存储SHA-256

判别口诀可以压缩成一句话:无密钥的可逆变换是编码,有密钥的可逆变换是加密,不可逆的摘要映射是哈希。

这一百八十年里的每一步都在某个约束下重新分配代价——没有哪种编码是免费的,只有哪种代价你愿意付。下一章把载体从符号换成像素:当读取编码的不再只有程序,还有激光、相机与人眼,同一套取舍要多掂量一层物理约束。

图形编码:四种密度的取舍

把信息编码进图像,是个频谱极宽的问题:宽到一维条形码几十年的进化只学会装十几个字符,密到一个边长三厘米的二维码能吞下一整个网址外加损坏三分之一的容错。本章沿密度这条轴排开四种图形编码——Code128、QR、favicon、OG 图,前两个给机器读,后两个给人看,工程决策却出奇地同构,都在为扫描方式、容错需求和传播场景做取舍。

QR 码的构图课

解码器面对一张二维码时,第一个问题不是「内容是什么」,而是「图在哪、转了多少度、缩放了多少」。

QR 码的答案画在三个角上。左上、右上、左下各嵌一枚 7×7 的回字形定位图案(finder pattern)。外圈黑框、中圈白环、中心 3×3 黑块,黑白比例严格为 1:1:3:1:1。

三枚图案有两个几何性质。任何三枚都能唯一确定一个仿射变换(平移、旋转、缩放全解出来),而任意两枚不能(90 度旋转会让两角互换产生歧义)。

三个角不是装饰,是 QR 码敢让用户斜着、歪着、隔着屏幕拍它的全部底气。

定位图案之间,一行与一列黑白交替的校正图形(timing pattern)像尺子一样标出模块网格的节拍。大尺寸版本还会在第四个角附近补对齐图案。

最外圈留白的静区(quiet zone)同样属于编码本体。解码器靠它把二维码从背景里切出来。QR 规范建议静区留 4 个模块宽;站内二维码生成的 margin 取 2,贴边打印时不如规范稳妥,但屏幕显示通常够用。这是「规范建议」和「实际够用」之间一次典型的有意识让步。

那些 1:1:3:1:1 的比例也不是审美决定。扫码的第一步常常不是「识别图形」而是「扫描线扫过明暗边界」。解码器沿着任意方向扫过定位图案,记录黑白跳变的宽度序列,1:1:3:1:1 在旋转、缩放、透视畸变下比例不变。

它是个相似不变量,相机离多远、歪成什么角度都读得出来。

这个设计和条形码首尾那些细长的警戒条(guard bars)一脉相承:都是给扫描器准备的「此处开始计数」的节拍器。

还有一个常被忽略的角色:紧贴右上定位图案下方和左下定位图案右侧的格式信息条。30 来个模块,记录本码使用的纠错等级和掩码编号,并且用 BCH 码另行保护、还与掩码异或着存了两份。

它必须比数据区更耐造。解码器读不出纠错等级,后面的一切都无从谈起。

二维码的设计层层都遵循同一条纪律:越靠近「能不能读」的决策信息,保护越厚。

尺寸维度上,QR 码分 40 个版本(version)。版本 1 是 21×21 模块,每升一版边长加 4,版本 40 达到 177×177(共 31329 个模块)。

容量按字符集分三档计价。版本 40 在最低纠错等级下能装 7089 个数字、4296 个字母数字混合字符或 2953 个字节。最常见的网址长度(几十字符)用版本 3~5 就绰绰有余。

编码器会自动选能装下的最小版本,格子越少越好扫。

纠错

二维码最超日常直觉的性质是:被遮住一角、沾了污渍、压了半个 logo,照样能扫出来。

这背后是 Reed–Solomon 纠错码,1950 年代为深空通信发明的老技术。

直觉化的讲法是:把数据看成一条多项式曲线上的采样点,纠错位是额外多测的一批点。曲线由数据唯一确定,所以哪怕一部分采样点被噪声砸歪了,只要剩下的点足够多,数学上能把原曲线重新拟合回来。

错误不是被「容忍」的,是被计算「修复」的。

严格的说法:每附加 2t 个纠错符号,可以纠正 t 个符号错误。全部运算发生在 256 个元素的有限域 GF(2⁸) 上,一个字节恰好一个符号。

这套机制与汉明码同宗。汉明码纠单个比特错,RS 把同样的思路推广到「纠整段字节错」,代价按需购买。

编码的压缩、容错、校验三大动机中,容错这条线的完整谱系在上一章校验与纠错一节里已经铺过。RS 是那条线上工程收益最高的一个节点。

「以字节为修复单位」这个细节比看上去重要。

噪声损伤图像时从来不是规规矩矩地按比特咬。一道划痕、一滴墨、一块 logo 遮罩,毁掉的总是连续的一片。

若按比特纠错,一个字节里错 3 位就是 3 个独立错误,修复成本×3。RS 以字节为符号,一整片被毁的连续字节算作一串符号错误,仍在 2t 预算内按个数赔偿。

它统治光盘、深空通信和二维码的缘故:它的敌人是突发错误,而不是散弹噪声。

QR 码还把数据块打散交错存储,让「遮住一个角」的损伤被摊薄到所有块里,每块分到的错误都在修复预算之内。纠错码负责修复,交错负责把灾难切小,两层机制咬合才有了「烂一角照样扫」的日常奇观。

QR 码按纠错强度分四级,命名直白。L 约可恢复 7%、M 约 15%、Q 约 25%、H 约 30% 的码字。

等级越高,同尺寸下装数据的余量越少。H 级几乎用三分之一的容量买了保险。

站内二维码生成的设置面板把这个选择直接暴露为下拉框(默认 M),配合前景色、背景色和 128–1024 像素的输出尺寸。想往二维码中央印 logo 的实践,前提就是把等级调到 H,给被盖住的模块预留被 RS 修复的份额。

文本输入带 300 毫秒防抖、改动即实时重绘,调试「网址里多一个斜杠会多占几个模块」这类小问题时手感极快。

顺便一提自定义颜色的边界。扫码相机靠亮度差读码,前景与背景必须保住足够的明暗反差。深底浅码在多数相机里照样能扫,但把两者调成同明度的红绿配色,纠错码再强也救不回对比度。这也是「纠错修数据、修不了物理」的又一例证。

纠错还解释了为什么二维码上的掩码(mask pattern)存在。数据区的黑白分布若凑巧形成大片同色或假定位图案,会干扰解码器。所以规范预置 8 种翻转掩码,编码器逐一打分、挑扣分最少的那种罩上去。

你看到的每一个二维码,都自动选过一次「最好看的噪声」。

这些设计共同指向一个工程哲学:为最坏的物理世界编码,而不是为理想的比特流编码。

顺着这个哲学还能解释一个印刷现场的高频事故:为什么海报上的二维码总被要求「再印大点」。

扫描的物理下限由每个模块在传感器上占到的像素数决定。同样一张二维码,张贴得越远、每模块的像素越少,纠错要救的「读错」就越多。版本越高的码模块越密,同样物理尺寸下每模块反而更小。

所以工程口诀是按扫描距离选版本和尺寸。远看的海报用低版本大尺寸,凑近扫的小票才用得起高版本。

纠错等级在这里不是越高越好。H 级的修复能力是用容量换的,版本被迫升高、模块被迫变密,远距离反而更容易翻车。

「容错、密度、尺寸」三个旋钮要一起拧,这是 QR 码工程里最接近杠杆原理的一课。

Code128

条形码的起点比 QR 早了半个世纪。1974 年,美国一家超市收银台第一次扫响 UPC 条形码,商品是一包口香糖。从那天起,「条纹即价格」进入了日常。

如今超市用的 EAN-13 是 UPC 的国际扩展,固定 13 位数字,左右两半各按奇偶结构编码。第一位数字甚至不直接画出条纹,而是由左侧六个字符的奇偶排列隐式携带。一维编码省带宽省到了这种地步。

站内条形码生成器实现的是 Code128 的 B 子集。编码器是一张手写的 107 行模式表:每个字符映射为一串 11 个模块宽的条空组合(如 212222 表示 2:1:2:2:2:2 宽度的六段条空交替)。

整条符号的结构是:起始符(B 子集固定为 104 号)+ 数据字符 + 1 个校验符 + 终止符(106 号)。

Code128 其实有三套子集。A 覆盖控制字符、B 覆盖大小写字母与符号、C 把两位数字打包进一个符号。纯数字的物流单号用 C 子集容量直接翻倍,这也是快递面单清一色长条数字码的原因。

工具只实现了 B 子集,够编码任意可见 ASCII 文本,遇到超出 32–126 的字符会安静跳过。

校验符的计算是逐位加权的。起始值 104 起底,每个数据字符按位置加权累加,最后对 103 取模。这个 mod 103 的余数直接当作表索引查出对应条空模式。

校验符和普通字符长得一样,混在条里毫不起眼,但扫描器会验它。它错一个位,整条码就地作废。

有意思的是,这份实现里位置权重的起点与规范的 1,2,3… 不同(源码里计数器从起始值 104 开始预增,首个数据字符的权重实为 105),模 103 之后的校验值因此偏离标准。条码图照样渲染,但严格校验的商用扫描器可能会拒绝识别。

手写编码器与规范之间差一行的位置,正是「能显示」和「能互换」之间的距离。这类细节只有对着源码和规范逐位核对才会现形。

容量对比是残酷的。Code128 覆盖 ASCII 32–126,每个字符固定消耗 11 个模块(外加起始、校验、终止约 3 个字符的开销)。一条 10 厘米宽的条形码实打实也就装二三十个字符。

QR 版本 1(21×21,仅 441 个模块)在 L 级就能装 41 个数字,版本 40 的 2953 字节更是四个数量级的差距。

但一维码活到今天不是靠怀旧。激光扫描器扫一维码只需一条扫描线,成本和功耗低到可以嵌进超市收银台和工厂流水线。传输方向上它天生抗拉伸,打印在弯曲的包装面上也不变形。

条形码用容量换了鲁棒和便宜,QR 码用扫描条件换了密度。各自的账都算得过来。

Favicon

favicon 的历史比大多数人记得的长。1999 年前后 IE 引入这套机制时,规格就一条:16×16 像素的 ICO 文件,放在网站根目录。

此后二十多年,这个 16 像素的画布不断被追加住户。浏览器标签页用 16/32,桌面快捷方式用 32/48,高分屏用 64 起步,iOS 主屏图标另起炉灶要 180×180,Windows 磁贴、macOS 触控板各有各的尺寸单。

一个「小图标」长成了多分辨率家族。ICO 格式干脆支持把多张不同尺寸的位图打进同一个文件,按需取用。

近年现代浏览器又收编了 SVG。一张矢量图自动适配一切尺寸,favicon 生成器们终于可以从「逐尺寸出图」转向「出一张图」。

站内的 Favicon 生成器走的就是 SVG 路线。输入一两个字母、选背景前景两个颜色,生成 256×256 的圆角 SVG。预览区并排渲染 16、32、48、64、128、256 六档尺寸供检查,满意后下载 favicon.svg。

它把 favicon 设计的真问题摆到了台面上:16 像素下还能认出什么?

256 档里精致的字形,缩到 16 档后笔画糊成一团。活过 16 像素审判的通常是粗壮的单字母、高对比的双色块和干脆的轮廓。这也是为什么大厂 logo 几乎都有一份「退化到像素画」的 favicon 专用版。

预览区那排从 16 到 256 的并排小图,本质上是一条自动化的「降级链压力测试」。

16 像素的苛刻源于物理。在浏览器标签页的常规缩放下,16×16 画布总共 256 个像素,每个「笔画」若小于 2 像素宽,抗锯齿一掺和就没了。一个字母要在 16 像素里可读,字号实际只允许 10–14 像素、且必须配粗体。

工具默认用 256×256 的 SVG 作画布再交给浏览器缩放,矢量保证任意缩放不糊。但「不糊」不等于「可读」。缩放解决了分辨率,解决不了信息带宽。

深浅两套主题又添了一层。同样的黑字白底图标,在深色标签栏里会变成一块突兀的白斑。现代做法是在 SVG 里内嵌媒体查询为两套主题各配一份配色。

生成器把前景背景完全开放给用户,等于把这个「标签栏实测」的最后一步留给人眼。生成只是起点,切到深色模式的浏览器里看一眼才算验完。

OG 图

前三节的角色都是「机器或人主动凑近来看」,最后一种编码反过来:它是页面递出去的名片。

一个网页被分享到微信、X、Discord、Slack 时,聊天窗口里自动展开的那张标题卡片,读的是页面头部元数据里的 og:image、og:title、og:description 等字段。

Open Graph 协议由 Facebook 在 2010 年前后定下,如今是不折不扣的跨平台事实标准。1200×630 像素是各家平台默认的画幅,约 1.91:1,正好填满时间线卡片的窗口。

这个画幅的物理意义值得掂量。1200 像素宽,意味着在手机上以约 2 倍密度显示时正好铺满一行。OG 图是按「手机屏幕第一眼」设计的规格。

它和 favicon 构成同一枚硬币的两面。favicon 优化的是「已打开的标签页里被认出来」,OG 图优化的是「还没打开的链接里被点进来」。前者带宽 16 像素,后者带宽是整个卡片,但都在为「注意力这个信道」编码。

静态站点生成器普遍把文章标题自动排版进 OG 模板,等于给每篇文章免费配了一张海报。这一点常被低估:链接被转发的场景里,卡片图是唯一在正文之前抵达读者的内容。

站内的 OG 图生成器按这个画幅生成。标题、副标题两个输入框,两个颜色 picker 合成对角渐变背景,一键生成 SVG 并下载 og.svg。

把它理解成「页面版二维码」最贴切。二维码是让人主动扫过来,OG 图是让链接在被转发的瞬间自带一张自我介绍。扫不扫在用户,但卡片显不显眼决定链接点不点得开。

运营过分享卡片的人很快会遇到它的两个「平台脾气」。

其一是缓存。各平台对 OG 数据抓一次存很久,改了图不换链接路径,卡片可能一直挂着旧版。所以改图要连 URL 一起改,或者用官方调试器手动触发重抓。

其二是字体。SVG 里写的 sans-serif 依赖渲染方的字体环境,把 SVG 直接转位图时,缺字体的机器会把标题排成完全不同的样子。批量出图的流水线要么把字体嵌进 SVG,要么用无头浏览器渲染保证所见即所得。

工具输出的 SVG 定位是设计稿与模板。文案、配色、布局在此定型,转成平台真正收的 PNG 再写进页面元数据,链路才算闭环。

SVG 在这里的真正价值是把「设计参数」(文案、渐变色)与「输出格式」解耦。模板一次定型,每篇文章改两个字符串就能出图,也就是静态博客批量配 OG 图的标准姿势。

密度、容错、成本、场景,四列约束四种解,没有一种全赢。

四者共享的底层动作也是同一个:把一段信息按某个读取者的能力建模。激光只能扫一条线、相机怕缺对比度、人眼在 16 像素里只认轮廓、时间线里的注意力只给卡片半秒钟。然后让编码向那个能力低头。


感谢您的阅读!如果可以,给俺点些关注吧~

编码简史与图形编码

周一 8月 31 2026
9012 字 · 31 分钟