前言
在Google上搜索”DPI vs PPI”可以看到非常多的关于这两个概念的讨论,而且讨论内容不仅仅是界面设计、软件开发的内容,也包含印刷、图像处理和其他内容。那么DPI到底是什么,和PPI又有什么区别呢?这篇文章,就让我来带你一探究竟。
DPI:Dots Per Inch。每英寸点数。
上面这个定义非常简洁,但是重点没说清楚:“点”/Dot是什么?这就是理解DPI的关键。
起源
DPI最初用于图像的印刷。图像如果要打印到纸上,实际上是通过一系列的墨点来绘制的,在“打印”这个场景中,墨点,就是DPI中D的概念。DPI的含义是点密度,用来描述打印的精细程度。
举个例子:目前常见的打印机,DPI是300。也就是说打印机能够在每英寸长度的纸上面,打印300个独立的墨点。这就是说,如果我们打印一个边长为1英寸的黑色正方形,那么打印机会在这个正方形的范围内,绘制300 × 300 = 90000个墨点。
你可以做个实验,然后会发现根本看不到墨点,为什么呢?因为墨点的间距太小了,而且墨点绘制到纸上之后会浸入纸张,和周围的墨点连到一起,所以墨点就分辨不出来了。
这便是DPI在印刷领域的定义。事实上DPI这个词最早就由印刷行业创建[1]。单色印刷通过控制墨点的密度,来区分不同的灰度,墨点的密度,就称作DPI。
最初的打印机是点阵式打印机(Dot Matrix Printer)[2],原理是使用撞针撞击墨带,墨带和纸接触将墨印到纸上。一根撞针将颜色印到纸上,就形成一个“点”。因为撞针的物理特性。不能做得很小,这类打印机的DPI很低,在70左右。
随后喷墨打印机(Inkjet Printer)[3]的出现,让DPI提高了非常多,甚至可以支持打印灰度、颜色。最常见的喷墨式打印机,常用DPI为300。有些打印机可以达到1200。过高的DPI对纸张也有了更高的要求。通常我们都在使用300DPI作为打印参数。
数字显示时代
PPI是数字显示时代的参数,PPI是Pixel Per Inch的缩写,也就是每英寸像素。这个指标用于表示屏幕的物理精细度。
DPI就是PPI
最早的支持GUI的电脑屏幕,比如施乐的Alto,苹果的Macintosh,屏幕的像素密度都是72PPI[4],这个值,就来自于当时的点阵式打印机的精度。因为PPI相同,所以屏幕上显示的一个像素,就对应打印出的纸上面的一个点,相同尺寸(5点)的文字,可以在显示器上得到所见即所得的效果。(当时,纸质文件是主要的信息交换方式)
在这个年代,DPI和PPI的含义是一样的,因为显示器上,图像的组成最小单位,就是屏幕上的一个物理像素。如果一张图片的尺寸为72×72,那么在屏幕上显示为1英寸,打印出来也是1英寸。
DPI不是PPI
如今,在显示领域DPI区别于PPI,要”归功“于微软当时的一个决策。因为眼睛和屏幕的距离,通常大于看纸张的距离(大约长30%)。所以虽然DPI和PPI一致能保证在屏幕上所见即所得,但直接观感上,屏幕上的内容相对于纸上会更小。
微软为了解决这个问题,在操作系统层面做了一个HACK:假定屏幕的PPI是96(因为当时大多数显示器都是72PPI,96 = 72 × 4/3)。所以运行在微软操作系统上的软件都认为这块显示器的PPI是96,以至于同样是10点(point)大小的文字,在实际上是72PPI的显示器上,最终实际看起来就会(相比于软件认为是72PPI,10像素)大1/3(13像素,实际用来显示文字的像素数变多了)。[5]
理解这个现象需要知道渲染分辨率的内容,可以看完下文再回过来看。这个场景中,逻辑像素密度是72,操作系统假定屏幕的PPI是96。渲染过程中,10点的内容会因为渲染像素密度是96,所以预期渲染的像素数量相比于PPI为72的情况就增加了1/3:10 / 72 * 96 = 13。实际显示到设备上时,将渲染画面点对点显示到物理显示设备上,物理尺寸就打了。(显示内容变少了)
短期来看微软解决了”保护视力“的问题,但正是因为微软的这一决策,让DPI和PPI的概念开始如此难以理解。
同时,也说明了DPI和PPI的一个最重要的区别:
- PPI是屏幕的固定参数。
- DPI是显示策略的一个参数,可以通过软件调整。
DPI的差异会影响使用密度无关单位(比如:点,point)设置尺寸的图像的显示。DPI > PPI,导致占用更多像素,整个屏幕显示更少内容,反之亦然。
更大的显示空间,还是更精细的图像
显示技术发展到现在,尤其2010年iPhone 4发布之后,PPI成为显示产品的一个重要竞争参数。高PPI可以带来更多的显示空间,也可以用来提升图像的显示效果。
从最初的72PPI/96PPI开始,后面几十年的发展,桌面显示器的PPI都提高到了110左右,相比于之前,增大的PPI带来的主要收益是显示空间的增大。举个例子:同样是17寸的显示器,1280 × 960分辨率相比于1024 × 768增加了像素数,同样也增大了PPI。因为都是17寸,虽然显示器的显示面积没有变大,但是像素数的增加使得屏幕可以显示更多内容,但是因为物理尺寸不变,界面元素就变小了。
在低PPI时代,操作系统默认会选择更大显示空间。长时间以来消费者已经习惯了低PPI的显示效果,厂商也不愿意创新,年复一年贩卖着低PPI的屏幕。
Retina Display
直到iPhone 4带着视网膜屏幕出现,才真正意义的让消费者感受到了高PPI带来的显示效果的提升。iOS的做法,是直接将PPI翻倍,使用4个像素来渲染原本仅用一个像素渲染的内容。对于文字来说,得益于矢量字体,文字的显示效果变得异常清晰,对于位图来说,高PPI的屏幕,也使得高质量的位图能够被显示。
事实证明,高PPI屏幕带来的显示效果提升是巨大的。高PPI移动设备的兴起,尤其是2013年MacBook Pro with Retina Display的发布,倒逼着桌面电脑厂商和显示器厂商生产更高PPI的屏幕。苹果使用移动、桌面两个平台的优秀体验,刷新了消费者对于显示的认知。不过,高PPI并不是一上来就完美。iOS和macOS视网膜屏幕推出之后很长时间,有很多应用仍然没有适配视网膜屏幕,也就是没有发挥高PPI的优势。
iOS和macOS对于没有适配的应用,在系统层面做了自动适配处理,就是对应用使用低PPI进行离屏渲染,然后将渲染结果拉伸到高PPI。这样的结果是:
- 功能可用。
- 图像有模糊和毛刺。
以下是适配视网膜屏幕之前、之后的Chrome的显示差异[6]:
和苹果的境遇不同,兼容机厂商无法控制操作系统,微软无法控制硬件。所以Windows对高PPI屏幕的支持要逊色一筹。以至于直到Windows 8.1发布,才可以实现在高PPI设备上充分利用高PPI的优势,显示更精细的图像,同时还能保证旧应用功能可用(并不是所有应用)。[7]
现在,不管是macOS还是Windows 10,都能充分利用高PPI显示更精细的图像,并且默认,macOS和Windows都将内容放大,使用更多像素渲染更少内容,也即选择了“更精细的图像”。其实,视网膜屏幕的效果,是在足够高的PPI下,实现了不减少显示空间的前提下,将显示精度提高了一倍。
高PPI下的DPI
目前的操作系统:Windows、macOS、iOS、Android等,都提供了或者部分支持修改DPI的选项,让用户选择更多显示空间,或者更精细的图像。因为物理参数已经固定了,所以更多显示空间就意味着元素相对更小,更精细的图像就意味着元素相对更大。当然,得益于物理参数的提升,相比于非Retina显示设备,均可以达到更高精度,更大空间。
DPI并不是真正的DPI
高PPI下,DPI还是那个DPI吗?为了介绍高PPI时代的显示策略,先介绍一个在高PPI下,从DPI衍生出的一个更容易理解的参数:scaling factor,缩放因子。
视网膜屏幕诞生之时(iPhone 4,也包含之后所有的3.5英寸、4英寸、4.7英寸的iPhone以及iPhone Xr),缩放因子为2,DPI == PPI。
为了方便说明,我们要对显示过程进行抽象。抽象出两个过程:渲染、输出;和三层抽象屏幕:逻辑层、渲染层、物理层。开发软件时,是在逻辑层上使用逻辑像素设置元素的长度。操作系统首先将逻辑层上的图像渲染到渲染层,得到渲染分辨率的图像;再将渲染层的图像,输出到物理层,也就是物理显示设备上。
- 逻辑层:使用逻辑像素作为尺寸单位。逻辑像素是密度无关的,可以理解为矢量单位。逻辑像素使用缩放因子转换成渲染像素对应。
- 渲染层:使用渲染像素作为尺寸单位。每个渲染像素储存渲染得到的位图的一个像素的信息。
- 物理层:使用物理像素作为尺寸单位。物理像素就是屏幕显示的最小物理单位。
- 逻辑分辨率:指逻辑层的逻辑像素数量。
- 渲染分辨率:指渲染像素数量。
- 物理分辨率:指物理像素数量。
场景分析
高PPI下,DPI实际指的是渲染层的渲染像素密度。如果渲染像素密度和物理像素密度恰好相等,那么DPI == PPI。不过,即便数值相等,含义还是不同的。下面通过在macOS,iOS,Android设备上的例子,来帮助理解这些概念,以及这些概念在操作系统实际显示过程中的相互作用。
macOS(Retina Display)
设备:MacBook Pro 2017
物理分辨率:2880 × 1800
屏幕尺寸:15.6英寸
macOS支持用户设置逻辑分辨率:
macOS系统在渲染时,缩放因子固定为2。以下示例中,为了获得渲染层的图像,可以简单通过全屏截图获得。
示例1:逻辑分辨率设置为1440 × 900
这个设置是MacBook Pro 2016以前的默认设置。
- 逻辑层:假设有一张1440 × 900的图像,比如桌面。或者任意状态下整个屏幕的内容。
- 渲染层:使用缩放因子为2进行放大,得到渲染分辨率:2880 × 1800。使用这个分辨率渲染矢量文字;根据2x缩放因子,查找图片素材(2倍图)并渲染图片。最终得到2880 × 1800的位图数据。
- 物理层:将渲染层的位图数据,点对点输出到2880 × 1800的屏幕上显示。
此时,DPI == PPI。
示例2:逻辑分辨率设置为1680 × 1050
这个设置是MacBook Pro 2016及以后的默认设置。
- 逻辑层:假设有一张1680 × 1050的图像,比如桌面。或者任意状态下整个屏幕的内容。
- 渲染层:使用缩放因子为2进行放大,得到渲染分辨率:3360 × 2100。使用这个分辨率渲染矢量文字;根据2x缩放因子,查找图片素材(2倍图)并渲染图片。最终得到3360 × 2100的位图数据。
- 物理层:将渲染层的位图数据,缩小到2880 × 1800的物理分辨率,并输出显示。
为了得到更大的显示面积,设置了更大的逻辑分辨率,因为缩放因子不变,渲染层渲染了超过物理分辨率的图像,DPI大于PPI(屏幕尺寸不变,分辨率增大)。虽然和Window早起的处理一样,增大了DPI,但因为这里并不是像Windows一样将渲染像素点对点显示到物理屏幕上,所以实际的效果是界面元素变小,使得显示空间变大。
因为物理分辨率(2880 × 1800)仍然大于逻辑分辨率(1680 × 1050),所以并没有丢失逻辑层的信息。
iOS
仅部分iOS设备支持修改逻辑分辨率,分别是4.7英寸、5.5英寸iPhone,iPhone Xr和iPhone Xs Max。并且仅支持将逻辑分辨率修改为逻辑分辨率更低的一档。比如5.5英寸的iPhone,可以将逻辑分辨率修改为4.7英寸iPhone的逻辑分辨率。和macOS一样,为了获得渲染层的图像,可以简单通过全屏截图获得。
示例1:5.5英寸iPhone,不开启放大模式。
设备参数:缩放因子 = 3,物理分辨率 = 1080 × 1920,PPI = 401。
- 逻辑层:逻辑分辨率为414 × 736。
- 渲染层:使用缩放因子为3,渲染分辨率为:1242 × 2208。
- 物理层:将渲染层的图像数据,缩小到1080 × 1920显示。
类似macOS的示例2,虽然最终显示有缩小,但实际上没有丢失逻辑层的信息。DPI为461。
示例2:5.5英寸iPhone,开启放大模式。
设备参数:缩放因子 = 3,物理分辨率 = 1080 × 1920,PPI = 401。
- 逻辑层:逻辑分辨率为375 × 667。
- 渲染层:使用缩放因子 3,渲染分辨率为:1125 × 2001。
- 物理层:将渲染层的图像数据,缩小到1080 × 1920显示。
类似macOS的示例2,虽然最终显示有缩小,但实际上没有丢失逻辑层的信息。DPI为417,相比上面的示例1,逻辑分辨率减少,显示内容变少,渲染分辨率变小,界面元素变大。可以看到macOS和iOS都是通过修改逻辑分辨率达到修改DPI,修改显示策略的目的。
示例3:iPhone XS Max,不开启放大模式。
设备参数:缩放因子 = 3,物理分辨率 = 1242 × 2688,PPI = 458。
- 逻辑层:逻辑分辨率为414 × 896。
- 渲染层:使用缩放因子 3,渲染分辨率为:1242 × 2688。
- 物理层:渲染分辨率和物理分辨率一致,点对点显示。
iPhone XS Max因为物理像素密度增大到458,屏幕像素数量增加,所以能实现在缩放因子为3时,渲染分辨率和物理分辨率点对点显示。实际上iPhone XS Max的逻辑分辨率宽度,和5.5英寸iPhone的逻辑分辨率宽度一致,这也是iPhone XS Max和5.5英寸iPhone在横向上能显示相同数量内容的原因。
按照这个规律,你可以分析一下缩放因子为2的iPhone Xr的显示策略。
Android
Android的情况就比较特殊了。Google和Windows的境遇比较相似,作为软件提供商,他们的操作系统会运行在不同的设备上,要应对不同PPI的屏幕。好在Android在设计之初就已经为不同PPI的屏幕适配设计了API,所以不会有早期Windows那样糟糕的兼容问题,但也带来了更高的复杂度。
Android和macOS、iOS固定缩放因子 + 指定逻辑分辨率不同。Android直接提供修改DPI的方法,厂商可以在构建操作系统固件时,指定DPI。DPI、缩放因子、逻辑分辨率的关系如下:
DPI = 厂商指定
缩放因子 = DPI / 160 // 160为基准DPI
逻辑分辨率 = (物理分辨率) / 缩放因子
Android同样提供缩放因子这个参数,同样,我们依然使用上面介绍的三层抽象模型进行分析。DPI参数,使用resources.displayMetrics.densityDpi
获取。
示例1:小米9,默认参数
设备参数:物理分辨率 = 1080 × 1920,PPI = 403,DPI = 440。
计算参数:缩放因子 = 440 / 160 = 2.75。
- 逻辑层:逻辑分辨率为:(1080 × 1920) / 2.75 = 392 × 698。
- 渲染层:缩放因子为2.75,渲染分辨率为:1080 × 1920。
- 物理层:渲染分辨率和物理分辨率一致,点对点显示。
基准DPI为什么是160在下文讨论。
可以看到,默认参数下,小米9的渲染分辨率和物理分辨率一致。那么修改了DPI会怎样呢?Android从7.0版本[8]开始在开发者选项中提供了名为”最小宽度“的修改项(最小宽度是逻辑分辨率的一部分,短边分辨率)。这个修改项的数值,就是逻辑分辨率中的短边值。小米9的默认值是392,我们将其改到480看下效果。
示例2:小米9,修改”最小宽度“为480
设备参数:物理分辨率 = 1080 × 1920,PPI = 403,DPI = 360。
计算参数:缩放因子 = 360 / 160 = 2.25。
- 逻辑层:逻辑分辨率为:(1080 × 1920) / 2.25 = 480 × 853。
- 渲染层:缩放因子为2.25,渲染分辨率为:1080 × 1920。
- 物理层:渲染分辨率和物理分辨率一致,点对点显示。
首先我们注意到DPI变成了360,为什么改了最小宽度,DPI的值会变化呢?实际上Android系统提供的修改最小宽度的方法,同样也是通过修改DPI实现的,只不过包装成了”最小宽度“(事实上就是在修改逻辑分辨率),系统在修改的时候换算成DPI,再修改系统参数。
其次我们看到因为Android使用动态的缩放因子,并且物理分辨率用作计算缩放因子的参数,所以反算出来的渲染分辨率,就是物理分辨率。这种做法的好处是避免了一次内存中的位图缩放操作。
因为逻辑像素的增加,屏幕可以显示更多内容,同时界面元素变小。
关于160DPI
为什么基准DPI的值是160呢?Android并没有官方解释,我猜测有一下几方面原因:
- iPhone 4之前的iPhone,PPI均为160左右。
- 初代Android设备的PPI为180,和160接近。
- 16 = 2 ^ 4。以4位基数进行计算时比较方便。(Android的ldpi标准,120 = 160 / 4 * 3,如果使用180作为baseline,那么ldpi = 135,显然不够规整)
- 16相比于18,对工程师更亲切些吧~
关于位图素材
因为Android的渲染策略是通过DPI指定的,所以Android对位图的使用策略,也是通过DPI来确定的。Android将DPI划分为了几个档:
开发者需要提供不同DPI的图片,使用相同的名称,如artboard.png,放到不同的目录下。应用运行时,使用R.drawable.artboard
引用这张图片。操作系统根据DPI设置选择合适的图片资源进行显示。系统的DPI和哪个档位的DPI值相近,就会使用哪个档位的图片资源。比如:
- 小米9,默认DPI为440,和480相近,那么会使用XXHDPI下的图片资源。
- 小米9,修改DPI为360(最小宽度480),那么会使用XHDPI下的图片资源。
密度无关像素 / 逻辑像素
iOS和Android开发中使用的长度单位,都是密度无关像素,或者称作逻辑像素。逻辑像素和渲染像素之间的转换关系是:
渲染像素 = 逻辑像素 × 缩放因子
之所以这里在使用渲染像素,而不是物理像素,是为了结论的普适性。对于Android来说:
1 dp = (resources.displayMetrics.density) px
DP之所以是密度无关像素,就是因为使用DP进行开发,不需要关系DPI,因为在逻辑层,没有密度的概念(可以将逻辑层的图像内容,理解为矢量内容)。
总结
看到这里,相信你已经对DPI的来龙去脉都有了比较清晰的了解。我们总结一下:
- 印刷领域的,DPI表示每英寸点数,点指的是墨点。
- 数字显示领域早期,施乐和苹果生产的设备使用和打印机1:1的72PPI屏幕,DPI的含义就是PPI,D指的是物理像素;Windows为了解决显示器显示内容偏小的问题,在操作系统层面“欺骗”软件当前显示器为96DPI,即便显示器的实际分辨率仍然为72PPI,此时D指的是“软件认为的”像素,也就是逻辑像素。
- 随着显示技术的逐渐提升,PPI的数值逐渐提高,操作系统允许用户修改DPI参数,来控制让操作系统显示更多内容,或者显示更精细的图像。
- 苹果发布视网膜屏幕之后,开启了缩放因子时代,厂商和消费者逐渐淡化DPI的概念,使用缩放因子(Windows)或者逻辑分辨率(iOS、macOS、Android)来控制显示策略。
- 不同操作系统的显示策略不同,iOS和macOS都是固定缩放因子,修改逻辑分辨率。Android是完全使用系统预设的DPI来计算缩放因子。
怎么样,仅DPI这个概念,随着科技发展,特别是消费电子的发展,也发生了非常大的变化。各厂商和操作系统对DPI的使用也不尽相同,从早期微软的虚拟DPI开始,DPI的概念变得越来越难理解。不过,理解DPI不是最终的目的,对于用户来说,了解如何利用DPI调节最适合自己的显示效果;对于开发者来说,了解如何利用DPI构建更精细、协调的界面,才是更有价值的事情。
希望本文能帮助到你。
参考
- [1] PPI vs. DPI: Demystifying the World of Online and Print Resolution
- [2] Dot matrix printing - Wikipedia
- [3] Inkjet printing - Wikipedia
- [4] [5] Where does 96 DPI come from in Windows? – fontblog
- [6] MacBook Pro Retina Display Analysis
- [7] Windows 8.1 DPI Changes - Scaling Windows - The DPI Arms Race
- [8] Android N has hidden DPI setting to activate tablet UI