抗锯齿入门指南

Paul Lewis

简介

抗锯齿在网络图形领域就像一个无名英雄;正是正因如此,我们的屏幕上才会有清晰的文本和平滑的矢量形状。事实上,有几种浏览器使用抗锯齿方法,这些方法在文本呈现方面最为明显。当用于防警报的算法开关时,可能会导致意外的视觉结果。在本文中,我们将介绍抗锯齿方法,并了解如何绘制像素。

我们都知道,所有的屏幕都是由像素组成的。这是一个由方块组成的巨大网格,每个方块均包含红色、绿色和蓝色 (RGB) 成分。离视点较远时,我们可以看到图片、文字和图标,但近距离观看时,就能实际看到 RGB 成分的网格以及各种元素是如何构成的。

特写屏幕的像素。每个像素都包含红色、绿色和蓝色的分量
图 1 - 屏幕的像素特写。每个像素都包含红色、绿色和蓝色分量。

防混叠

那么,当我们绘制矢量形状并穿过像素的“部分”时,会发生什么情况呢?假设我们要绘制的形状是黑色,背景是白色。我们应该为该像素着色吗?如果要为它设置颜色,应该使用哪种颜色?黑色、灰色,还是其他颜色?

抗锯齿过程决定了我们在填充像素时应使用哪一种颜色。其中最简单的版本叫做灰度抗锯齿,它会平等处理像素的三个分量。因此,如果像素被遮挡一半(假设为黑白文本持续一秒钟),以简单起见,您会认为每个组件都会设置为半亮度(我知道我确实这样),但实际上要比这更复杂:您必须考虑灰度系数,这意味着您可能永远不会将其设置为该确切值。这当然会使情况变得更加复杂,但由于这里仅是对这个主题的简介,所以在此不作深入探讨。需要注意的是,灰度抗锯齿是在像素级别进行处理的,事实上我们可以做得更好。

图 2 - 抗锯齿与硬边缘
图 2 - 抗锯齿与硬边缘

在图 2 中,您可以看到同样绘制的三角形,但左侧启用了抗锯齿,右侧已停用。如您所见,启用抗锯齿后,如果三角形仅穿过像素的一部分,像素呈现灰色阴影。不过,停用后,像素会填充为纯黑色或纯白色,并且形状看起来呈锯齿状。

文本呈现

每当浏览器渲染文本(本质上是矢量形状)时,就会遇到相同的问题:文本字符只会部分填充某些像素,所以我们需要制定一个有关如何填充这些像素的策略。理想情况下,我们希望文本有消锯齿,这样便于阅读,让人感觉更愉悦。

不过,灰度级抗锯齿方法只是处理该效果的一种方法。通常采用的方法是在启用像素的 RGB 分量方面更有选择性。这个过程称为子像素抗锯齿,多年来,Microsoft 的 ClearType 团队投入了大量的时间和精力来改进它。现在,它的使用范围更广,所有主流浏览器都对它使用程度高低。

首先,因为我们知道每个像素实际上是由单独的红色、绿色和蓝色成分组成的,所以我们检测到对于相关像素,应“打开”其中每个成分的程度。因此,如果某个像素从左侧“半遮挡”,我们就可能完全打开红色分量,将绿色分量完全打开,将蓝色分量关闭。此过程通常被称为“屏幕水平分辨率的三倍”,并且每个像素实际上都是三个独立的组件,而不是一个单元。

图 3 - 使用灰度模式与子像素防锯齿
图 3 - 使用灰度模式与子像素防锯齿

在上面的图 3 中,您可以看到左侧我们平等对待每个组件,并且每个组件都平等地开启或关闭(灰度模式)。不过,在右侧,我们使用了子像素方法,根据每个组件(红色、绿色和蓝色)与所绘制形状的重叠程度,使其以不同的方式呈现。

然而,尽管如此,人类视觉的红光、绿光和蓝光实际上并不相等。我们对绿色比红色或蓝色更加敏感,这意味着虽然灰度抗锯齿确实有优势,正如 Darel Rex Finley 指出的那样,单独启用每个组件实际上并不会带来 3 倍的清晰度提升。不过,亚像素抗锯齿功能绝对很有用,这确实意味着与使用灰度抗锯齿相比,我们可以更清楚地看到文字。

图 4 - 亚像素抗锯齿文本。启用像素的各个组件,以呈现整体效果
图 4 - 亚像素抗锯齿文本。启用像素的各个组件,以打造整体效果

切入正题

这对我们开发者来说意味着什么?从 Chrome 的角度来看,至少会同时使用灰度和亚像素抗锯齿来渲染文本,而哪种方法取决于几个条件。不过,首先我们需要对层有所了解,因为这是主要标准。如果您尚未了解各层以及这些层在 Chrome 内部是如何使用的,那么 Tom Wiltzius 撰写的关于该主题的精彩介绍是您应该先阅读的一篇。

假设您熟悉图层或刚刚了解图层,让我们继续。如果网页启用了硬件合成,并且您在不是根层的层上有文本内容,则默认情况下将使用灰度抗锯齿功能渲染该层。开发者经常会注意到,如果对元素运用黑客手段,使元素嵌入自己的(非根)层(例如使用 TranslateZ),那么他们会看到文本的呈现方式有所不同。开发者经常会通过 JavaScript 或 CSS 即时应用“新图层”触发器,导致文本渲染从子像素切换到灰度模式;如果您不知道是什么触发了渲染更改,可能会感到困惑。但是,如果文本位于根层中,则应使用亚像素抗锯齿进行渲染,从而使得阅读更加清晰。

但是,与网络世界一样,它也在不断发生变化。在 Chrome 中,系统会为非根层中的文本启用子像素抗锯齿功能,但前提是相应层满足三个条件。值得一提的是,这些标准现在适用,但它们可能会发生变化,随着时间的推移,您应该看到涵盖的案例更多。目前,这些标准包括:

  1. 该图层具有完全不透明的背景颜色。值得注意的是,使用 border-radius 或非默认 background-clip 值会导致该层被视为非不透明,并且文本渲染会还原为灰度抗锯齿。
  2. 层只能应用身份转换或积分转换。我们所说的积分是指舍入值。例如,translate(20.2px, 30px) 会导致灰度抗锯齿,因为 x 分量 20.2px 不是整数。恒等式转换仅表示,除了默认转换之外,没有应用额外的旋转、平移或缩放。
  3. 层的不透明度为 1.0。任何不透明度的变化都会将抗锯齿从亚像素变为灰度。
图 5 - 前后对比:灰度与子像素。请注意右侧文本的色边
图 5 - 前后对比:灰度模式与子像素。请注意右侧文本的色边

最后要注意的一点是,应用 CSS 动画可能会导致系统创建新层,而使用 requestAnimationFrame 则不会。对于某些开发者,隐含的文本渲染差异导致无法使用 CSS 动画。因此,如果您一直因文本呈现差异而使用 JavaScript 为元素添加动画效果,不妨看看这项更新能否帮您解决问题!

以上就是 Chrome 的介绍就其他浏览器而言,Opera 在迁移到 Chromium 的过程中应该与 Chrome 的行为高度保持一致。Internet Explorer 似乎对几乎所有文本使用了亚像素抗锯齿(当然,如果您已启用 ClearType!),尽管似乎不在 Windows 8 的 Metro 模式下使用。由于 WebKit 与 Blink 非常相似,因此 Safari 的行为方式与 Chrome 非常相似,只不过没有这些可以实现更多亚像素抗锯齿的新改进。Firefox 的行为与 Internet Explorer 大致相同,因为它对几乎所有文本都使用了亚像素抗锯齿。当然,这并不是完整的列表,并且可能在所有浏览器中会使用灰度抗锯齿而不是子像素,不过要知道,子像素抗锯齿功能在主流浏览器中都被广泛使用。

总结

至此,您对抗锯齿功能的工作原理以及如今在网站和应用中(尤其是在 DPI 较低的设备上)可能会出现文本渲染差异的原因。如果您有兴趣继续了解 Chrome 的文本渲染实现,请对以下 bug 加注星标:

资源和参考文档