java 全局字体抗锯齿_云风的 BLOG

原文讲解的非常细致,还配了不少图片,我就不再重复了,只简单说两句。

我认为发明这个算法的动机是 “Our UI has a lot of smooth animation, text should be able to move smoothly across the screen.” 这是过去很多传统字体渲染算法很难解决的。

当我们输出的文字略微偏移 1/4 个屏幕像素时,通常会糊掉。但是这个算法则可以尽可能地保留信息。

因为这个算法的本质是把 16 倍大的字形,以黑白二值位图的形式保存在一个每像素 16bit 的贴图中。和传统的保存字形位图的方式不同,它不储存字体抗锯齿后的灰阶图片,而是一个 bit 保存一个像素。贴图的一个图素保存了 4x4 共 16 个像素。把 16 个点阵像素压缩到贴图的一个图素中带来的并不仅仅是压缩的好处,更重要的是,可以让 gpu 做采样时,可以一次拿出更多的字形信息,方便做抗锯齿处理。

我们在纹理采样时,从贴图上采到的一个图素其实包含了 16 个字形像素的信息。如果我们采样 4 次,就可以拿到 16x16 = 256 像素的位图。然后,计算出真正采样的范围,统计这个采样区域 bit 1 的个数,就可以精确的算出灰阶。

相比传统储存方法,偏移部分像素,就只能通过简单的混合算法求出平均灰阶,这种储存方法相当于推迟了字型抗锯齿计算灰阶的时机。

我做了一个简单的动画,让一个 23 像素高的 “好”字在一个像素范围内轻微位移转圈,我们可以看到,好字的包围盒并没有扩大,但肉眼明显能感知到字的移动。

b0f1e8c312d47e4129638ef4ff9052ae.gif

我们把好字的半像素位移展开, X Y 方向各移动 1/4 1/2 3/4 像素,得到 16 个字,是这样的:

0df71fe643f2e538c0ee9bdb48352b5a.png

放大点可能看的更清楚:

d1e18eac8106c46298be7e5ce3f36a9f.png

如果我们把灰阶改成类似微软 ClearType 的方法,利用液晶屏 RGB 三个子像素,可以做到更清晰。这部分工作我懒得做了。

但我认为,这个方法对于目前游戏并没有太大意义。因为用 GPU 实现过于复杂,而收益(文字移动平滑这个特性)并不大。

而且算法本身有个弱点,原文并没有解决。那就是当字体放大或缩小时,其实贴图到帧缓冲的算法就不再是 1:1 的关系。如果按原文的方法 4x4=16 像素的窗口去采样,结果是不对的。如果最终显示的文字比原始字型点阵要小,就应该用大于 4x4 的采样窗口;如果要放大原始点阵,则应该用更小的采样窗口。

这个方法的极限可以把字形放大 4x4 倍而不失真。当你需要输出 4x4 倍大的文字时,其实每次只应该在贴图上采样一个 bit (1x1 窗口)。

用 CPU 可以勉强解决这个缩放问题,GPU 来做也未尝不可。我构想的方法是另外做一种带 mipmap 的纯色贴图,不同的 mipmap 层填充不同的颜色通道,比如第一层用纯红,第二层用纯绿,然后根据采样的颜色结果的红绿比例,可以推测出贴图到帧缓冲的映射比。

对英文来说,缩放问题并不麻烦。只要可以快速生成需要的大小的贴图(这也是原文第三篇讨论的话题),加上合适的 cache 即可;但是中文字形太多,我们就需要太大的 cache 。这也是我认为该算法(对中文)并不实用的原因之一。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值