抗锯齿入门指南

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 加注星标:

资源和参考文档