谈谈 HiDPI —— 是什么,为什么,怎么做

如果你在搜索引擎中输入「HiDPI」,那么你只会找到一堆和黑苹果有关的内容,和一堆注入 EDID 的「一键开启 HiDPI 脚本」。但是,到底什么是 HiDPI?什么是「用四个像素渲染一个像素」?我凭自己的拙见水一篇文章,粗浅地介绍一下 HiDPI 的定义、macOS「缩放」的原理,以及相关的逻辑。

HiDPI 的定义 —— 什么是 HiDPI?

HiDPI 其实是一个缩写,全称是 High Dots Per Inch,字面意思就是「每英寸包含数量更多的像素」,通俗点讲也就是 分辨率特别高但是尺寸并不大的屏幕,在市场上被称为 Retina(视网膜)屏幕。所以 HiDPI 的意思就是像素密度特别大的屏幕,好了,全文完(被打)

hidpi-0.jpg

实际上人们在说 HiDPI 时,他们说的其实是图片里的东西,也就是 macOS 中的「缩放(Scaled)」。本文要讨论的「HiDPI」也就是 —— 苹果是如何在分辨率更高的 Retina 视网膜屏幕上渲染和传统屏幕看起来大小相同、却更加清晰的图象的。

名词解释

为了接下来的描述,首先要定义几个名词:

  • 硬件分辨率:由你的显示器物理硬件决定的分辨率。比如我的 ThinkPad E480 有一块 14 寸的 FHD 屏幕,分辨率是 1920x1080,那么我说这块屏幕的硬件分辨率是 1920x1080。同理,我们可以说 2019 年发布的 16 寸 MacBook Pro 的硬件分辨率是 3072x1920、2019 年发布的 13 寸 MacBook Pro 的硬件分辨率是 2560x1600。
  • 硬件像素:你的显示器上的一个一个实际存在的像素点,在 LCD 屏上一组三个液晶构成一个硬件像素,OLED 屏幕上一组 3~4 个三色的发光二极管构成一个硬件像素。硬件像素组成了硬件分辨率,如 1920x1080 个硬件像素构成了一块 FHD 屏幕。
  • 逻辑分辨率:我们的大脑觉得画面的分辨率是多少。比如说在一块 14 寸的 UHD 屏幕(3840x2160)的屏幕上显示的窗口和内容数量,和在一块 14 寸硬件分辨率为 1920x1080 的的 FHD 屏幕上的同一个窗口的大小以及内容数量是一样的,那么我们说这块 UHD 屏幕的逻辑分辨率是 1920x1080。
  • 逻辑像素:逻辑像素组成了逻辑分辨率,正如硬件像素组成了硬件分辨率。一块 UHD 屏幕的逻辑分辨率是 1920x1080,意味着这块屏幕在显示图象时包括了 1920 乘以 1080(也就是 2073600)个逻辑像素。

需要注意的是,「逻辑分辨率」根本不存在,一切都是「我觉得」——「我觉得这块 4K 屏幕能显示的内容和 1080P 的屏幕是一样多的,不过 4K 屏幕上的画面看起来要更清晰」。在 macOS 界面中相关的说明文字用的词语也是「Looks like」(看起来像是)。

hidpi-1.jpg

看到这张图的「Looks like 1680x1050」(看起来像 1680x1050)了么?这里的 1680x1050 就是刚才定义的「逻辑分辨率」。

「逻辑分辨率」是不存在的,那么基于「逻辑分辨率」而定义出来的名词 「逻辑像素」自然也是不存在 的。

什么是「用四个像素渲染一个像素」?

hidpi-2.jpg

相信很多人都看过上面这张图,也有不少人听说过「用四个像素渲染一个像素」这种说法。那么,这句话到底是什么意思呢?

下图是一台 MacBook Pro,上面显示了一个大大的红色的圆。现在让我们简化一下模型,假设这个红色的圆只能用 8x8 个硬件像素来渲染,那么四分之一个圆则要用到 4x4 个硬件像素。

hidpi-3.png

如上图所示,如果只用 4x4 个像素显示一个四分之一圆,圆在每个像素中占据的面积是不同的,在显示时,需要根据圆在每个逻辑像素中占据的面积、通过算法决定该像素的颜色、对边缘进行「虚化」以补偿视觉效果。

macOS 的 Smooth Font 和 Windows 的 ClearType,依然是基于这种「虚化边缘」的方式试图改善字体显示的清晰度。

hidpi-4.png

然后有一天,你买了一台带 Retina 屏幕的 MacBook Pro,新的屏幕大小不变、但硬件像素的数量翻了四倍 —— 也就是说,之前那块屏幕上 4x4 个硬件像素的面积、在新的屏幕上已经可以容纳 8x8 个硬件像素了。要是在新的 Retina 屏幕上还用 16 个硬件像素渲染一个四分之一圆、那么圆的直径就只有之前的一半、面积就只有四分之一了。

为了确保相同的屏幕上显示内容的密度(在这个例子中,就是这个圆的面积不发生改变),我们可以让新屏幕的四个硬件像素显示旧屏幕一个硬件像素的内容(此时 Retina 屏幕上的这四个硬件像素显示的颜色完全相同),也就是让一个逻辑像素 直接拉伸对应 四个硬件像素,如 中间的网格图所示。现在圆的面积的确和之前一样大了,但是显示效果(清晰度)没有得到任何提升
如果要在 Retina 屏幕上达成 右边那张网格图 的显示效果,我们还需要一些别的操作。

hidpi-5.png

如果要提高画面清晰度,画面在渲染时的逻辑像素应该和硬件像素一一对应起来。原来的四分之一圆对应了 4x4 个逻辑像素(如上图 左边的网格图 所示)。如果我们先将画面放大 4 倍,这个四分之一圆就要对应 8x8 个逻辑像素了。再将画面渲染到 8x8 的逻辑像素上(如上图 中间的的网格图 所示)、最后一一对应的显示到 8x8 个硬件像素上(如上图 右边的网格图所示)。现在,相比直接将 4x4 拉伸到 8x8,搭配 HiDPI 的 Retina 屏幕还是用 8x8 个硬件像素渲染一个四分之一圆,圆的面积依然没有发生改变,但是轮廓处「虚化」像素的数量变少了、圆的边缘显得更锐利了

在传统屏幕上 1 个硬件像素中显示的内容,在 Retina 屏幕上需要用 4 个硬件像素,这就是 Retina 屏幕「用四个像素渲染一个像素」的含义。我们在使用 macOS 截图的时候,截取的正是图象放大四倍后这 8x8 个逻辑像素(而不是放大前的 4x4 个逻辑像素),因此 我们在截图的时得到的图片大小比屏幕显示的分辨率要大

等等,那么非整数倍的缩放呢?

如果你还记得上面那张图的话 —— 什么,你忘了?那我把那张图再放一遍:

hidpi-1.jpg

这是 2019 年 13 寸 MacBook Pro 的「系统偏好设置」界面中的显示相关设置。2019 年 13 寸 MacBook Pro 的屏幕的硬件分辨率是 2560x1600。如果四个硬件像素渲染一个逻辑像素,那么这台 MacBook Pro 进行「HiDPI 缩放」后的逻辑分辨率应该只有 1280x800。但是这张截图却直接告诉你,macOS 有能力在 2560x1600 硬件分辨率的屏幕上上显示 1680x1050 逻辑分辨率的画面。

毫无疑问,硬件像素是一个一个独立存在、不能拆开的 —— 没有人能点亮 1.5 个硬件像素。所以非整数倍的缩放是怎么实现的呢?让我们用逆向思维来思考这个问题。我们的最终目标是 将硬件分辨率 2560x1600 全部都利用起来,这是大前提;为了完全利用 2560x1600 的硬件分辨率,那么原始逻辑分辨率放大 4 倍后应该包含 2560x1600 个逻辑像素,所以 放大四倍前的原始逻辑分辨率应该是 1280x800 个像素。但是为了能容纳 1680x1050 的逻辑分辨率,需要 将 1680x1050 线性缩放到 1280x800,即需要先将原始画面缩小到原来的 76.2%。

通过逆向思维,我们把数字算出来了,现在让我们理一理这个流程。假设我们在 1680x1050 的逻辑分辨率下有一个边长为 500 个逻辑像素的正方形窗口,macOS 的 WindowServer 会首先将这个正方形窗口缩小到原来的 76.2%,也就是 381 个逻辑像素。接着将这个边长 381 个逻辑像素窗口的边长放大到原来的两倍(即面积放大到原来的四倍),也就是正方形窗口边长变成了 762 个逻辑像素。窗口面积放大四倍后,这 762x762 个逻辑像素和硬件像素是一一对应进行显示的。所以,在这台 2019 年的 13 寸 MacBook Pro 上,边长 500 个逻辑像素的窗口实际上需要用到 762x762 个硬件像素,硬件像素的数目比逻辑像素的数量更多、硬件分辨率高于原始逻辑分辨率,我们看到的窗口画面更清晰了。

你理解了吗?你以为你好像理解了,不就是「先按照一定百分比缩小、经过缩小后的画面再放大四倍、就和硬件像素一一对应」了嘛。那我现在把数字换一下,还是这台 2019 年的 13 寸 MacBook Pro,不过现在正方形窗口的边长是 100 个逻辑像素而不是 500 个,乘以 76.2% 得到 76.2、再把边长放大两倍(也就是面积放大四倍)得到 152.4 个像素。如果要和硬件像素一一对应的话,这 0.4 个像素怎么办,难道要扔掉吗?
所以要注意一点,我之前举的例子中使用了「缩小到原来的 76.2%」这种说法,纯粹是为了便于大家理解「先缩小再放大四倍渲染」的流程,实际上 这个说法是完全错误的

如果你接触过 iOS、macOS 应用开发的话,应该会注意到,在 macOS、iOS 应用中的图片和图标资源的大小使用的单位并不是 Pixels(px,像素),而是 Points。和像素 Pixels 必须是正整数不同,Points 可以是浮点数、也就是允许拥有小数。Points 的单位只代表一种 比例关系、和现实世界中(或者显示器屏幕上)占据的实际大小 没有任何关系,macOS、iOS 会自行计算一个 Points 应该对应的像素数量。所以所谓「缩小 76.2%」,本质上是一个 Points 需要对应的 Pixels 数量减少了而已。除此以外,macOS、iOS 在渲染图象时还会通过 vector 方法保留画面的线条、并得到更加锐利的边缘。

参考资料

本文作者 : Sukka
本文采用 CC BY-NC-SA 4.0 许可协议。转载和引用时请注意遵守协议、注明出处!
本文链接 : https://blog.skk.moe/post/hidpi-what-why-how/

喜欢这篇文章?为什么不考虑打赏一下作者呢?




评论加载中 ...