DPI 和 DIP

本文介绍物理像素和设备无关像素 (DIP) 之间的差异,以及如何在 Win2D 中处理 DPI (每英寸) 点数。

Win2D 的设计方式使许多应用可以忽略此区别,因为它提供了明智的默认行为,在低 DPI 和高 DPI 设备上运行时,这些行为将执行正确的操作。 如果你的应用有更专业的需求,或者如果你对“明智的默认值”的含义有不同的看法,请继续阅读关于流利的详细信息...

什么是 DPI?

DPI 代表“每英寸点数”。 这是输出显示器(如计算机监视器或手机屏幕)像素密度的近似度量值。 DPI 越高,构成显示器的点越多、越小。

DPI 只是一个近似度量值,因为并非所有显示硬件都可以依赖来报告准确的信息。 某些计算机监视器根本不会向操作系统报告 DPI,或者用户可能已将其系统配置为使用不同于实际硬件的 DPI 进行呈现,例如, (更改 UI 文本元素) 的大小。 应用程序可以使用 DPI 来选择应绘制的内容大小,但不应依赖它作为显示大小的精确物理度量。

DPI 值 96 被视为中性默认值。

什么是像素?

像素是一个彩色点。 计算机图形中的图像由排列在二维网格中的许多像素组成。 可以将像素视为生成所有图像的原子。

像素的物理大小因显示器而异。 当计算机连接到大型但分辨率较低的显示器或外部显示器时,像素可能相当大,但在具有 1080p 显示器的手机上,只有几英寸宽,像素很小。

在 Win2D 中,每当看到使用整数数据类型指定位置或大小的 API (或结构(如 BitmapSize)包含整数) ,这意味着 API 以像素单位运行。

大多数 Win2D API 使用 DIP 而不是像素。

什么是 DIP?

DIP 代表“与设备无关的像素”。 这是一个虚拟化单元,可能与物理像素相同、更大或小于物理像素。

像素和 DIP 之间的比率由 DPI 确定:

pixels = dips * dpi / 96

当 DPI 为 96 时,像素和 DIP 相同。 使用更高的 DPI 时,单个 DIP 可能对应于多个像素 (或部分像素,在 DPI 不是 96) 的确切倍数的常见情况下。

大多数Windows 运行时 API(包括 Win2D)使用 DIP 而不是像素。 这样做的优点是,无论在什么显示器上运行应用,图形的物理大小都保持大致相同。 例如,如果应用指定一个按钮的宽度为 100 DIP,则当在高 DPI 设备(如手机或 4k 监视器)上运行时,此按钮将自动缩放为宽度超过 100 像素,因此它仍然是用户可单击的合理大小。 另一方面,如果以像素为单位指定了按钮大小,则这种高 DPI 显示器上的按钮大小会显得非常小,因此应用必须执行更多工作才能针对每种屏幕以不同的方式调整布局。

在 Win2D 中,每当看到使用浮点数据类型指定位置或大小的 API (或包含浮点值的 Vector2 或 Size 等结构时,) ,这意味着 API 在 DIP 中运行。

若要在 DIP 和像素之间转换,请使用 方法和 ConvertDipsToPixels(Single, CanvasDpiRounding)ConvertPixelsToDips(Int32)

具有 DPI 的 Win2D 资源

包含位图图像的所有 Win2D 资源也具有关联的 DPI 属性:

所有其他资源类型都独立于 DPI。 例如,单个 CanvasDevice 实例可用于绘制到许多不同 DPI 的控件或呈现目标,因此设备没有自己的 DPI。

同样, CanvasCommandList 没有 DPI,因为它包含矢量绘制指令,而不是位图图像。 DPI 仅在光栅化过程中发挥作用,将命令列表绘制到具有 DPI) 的呈现目标或控件 (。

控制 DPI

Win2D 控件 (CanvasControlCanvasVirtualControl) CanvasAnimatedControl 自动使用与运行应用的显示器相同的 DPI。 这与 XAML、CoreWindow 和其他Windows 运行时 API 使用的坐标系匹配。

如果 DPI 更改 (例如,当应用移动到其他显示) 时,控件将引发 CreateResources 事件并传递 CanvasCreateResourcesReasonDpiChanged。 应用应通过重新创建依赖于控件 DPI 的任何资源(例如 rendertargets) ) (来响应此事件。

呈现目标 DPI

可绘制到 (不仅 CanvasRenderTarget 包括且包括类似呈现目标的类型 CanvasSwapChainCanvasImageSource) 具有自己的 DPI,但与控件不同,这些类型不直接连接到显示器,因此 Win2D 无法自动确定 DPI 应该是什么。 如果要绘制到稍后将复制到屏幕的呈现目标,你可能希望该呈现目标使用与屏幕相同的 DPI,但如果出于某种其他目的进行绘制, (例如。生成图像以上传到网站) 默认 96 DPI 会更合适。

为了简化这两种使用模式,Win2D 提供了两种类型的构造函数重载:

CanvasRenderTarget(ICanvasResourceCreator, width, height, dpi)
CanvasRenderTarget(ICanvasResourceCreatorWithDpi, width, height)

接口 ICanvasResourceCreatorCanvasDevice 以及 Win2D 控件实现。 由于设备本身没有任何特定的 DPI,因此在从一个呈现目标创建呈现目标时,必须显式指定 DPI。

例如,创建默认 DPI 呈现目标,其中 DIP 和像素始终相同:

const float defaultDpi = 96;
var rtWithFixedDpi = new CanvasRenderTarget(canvasDevice, width, height, defaultDpi);

ICanvasResourceCreatorWithDpiICanvasResourceCreator通过添加 DPI 属性进行扩展。 此接口由 Win2D 控件实现,可以轻松地创建呈现目标,该目标将自动继承与创建它的控件相同的 DPI:

var rtWithSameDpiAsDisplay = new CanvasRenderTarget(canvasControl, width, height);

位图 DPI

CanvasBitmap与呈现目标不同, 不会自动从控件继承 DPI。 用于创建和加载位图的方法包括显式指定 DPI 的重载,但如果省略此项,则无论当前显示配置如何,位图 DPI 默认为 96。

位图与其他类型不同的原因是它们是输入数据的源,而不是将绘制到的输出。 因此,位图很重要的不是输出最终位置的 DPI,而是源图像的 DPI,这与当前显示设置完全无关。

如果加载 100x100 的默认 DPI 位图,然后将其绘制到呈现目标上,则位图将从 100 个 96 DPI (10) 0 个 DIP 缩放,即目标呈现目标 (的 DPI 为 100 个 100 个 DIP,如果它是高 DPI 呈现目标) ,则其像素数可能更大。 生成的图像大小 (始终为 100 个 DIP,因此) 不会有令人不快的布局意外,但如果将低 DPI 源位图纵向扩展到更高的 DPI 目标,则可能会出现一些模糊。

为了在高 DPI 下获得最大清晰度,某些应用程序可能希望以不同的分辨率提供多组位图图像,并在加载时选择与目标控件的 DPI 最匹配的版本。 其他应用可能更愿意只提供高 DPI 位图,在较低 DPI 显示器上运行时让 Win2D 缩小这些位图, (纵向缩减通常比纵向扩展) 看起来更好。 在任一情况下,都可以将位图 DPI 指定为 的参数 LoadAsync(ICanvasResourceCreator, String, Single)

请注意,某些位图文件格式包含自己的 DPI 元数据,但 Win2D 会忽略这一点,因为它通常设置不正确。 相反,加载位图时必须显式指定 DPI。

CanvasDrawingSession DPI

CanvasDrawingSession 从绘制到的任何控件、呈现目标、交换链等继承其 DPI。

默认情况下,所有绘图操作都在 DIP 中运行。 如果希望以像素为单位工作,可以通过 属性更改这一 Units 点。

效果 DPI

图像效果管道从绘制到的任何 CanvasDrawingSession 效果中继承其 DPI。 在内部,效果处理始终以像素为单位运行。 参数值(如大小或位置)在 DIP 中指定,但这些单位在进行任何实际图像操作之前会转换为像素。

将 DPI 与目标绘图会话不同的位图用作效果源图像时,会自动在位图和效果之间插入内部 DpiCompensationEffect 。 这会缩放位图以匹配目标 DPI,这通常是你想要的。 如果不是你想要的,可以插入自己的 实例 DpiCompensationEffect 来自定义行为。

注意

如果实现自定义效果,请考虑应用等效的 DPI 处理方案,以确保与内置 Win2D 效果一起使用时的行为一致。

合成 API

API Microsoft.Graphics.Canvas.Composition 的运行级别低于 Win2D XAML 控件,因此它们不会尝试代表你自动处理 DPI。 由你决定你喜欢在哪些单元中操作,并设置在合成可视化树中实现该操作所需的任何转换。

Windows.UI.Composition API(如 ) CreateDrawingSurface 始终以像素单位指定大小。 使用 Win2D 绘制到合成图面上时,可以指定调用 CreateDrawingSession(CompositionDrawingSurface, Rect, Single)时要使用的任何 DPI。 通过返回 CanvasDrawingSession 的 执行的所有绘图都将相应地纵向扩展或缩减。

如何测试 DPI 处理

测试应用是否对更改显示 DPI 执行正确操作的最简单方法是在Windows 10或Windows 11上运行,并在应用运行时更改显示设置:

  • 右键单击桌面背景,然后选择“显示设置”
  • 移动标记为“更改文本、应用和其他项目的大小”的滑块
  • 单击“应用”按钮
  • 选择“稍后注销”

如果没有Windows 10或Windows 11,还可以使用 Windows 模拟器进行测试。 在 Visual Studio 工具栏中,将“本地计算机”设置更改为“模拟器”,然后使用“更改分辨率”图标在以下两者之间切换模拟显示:

  • 100% (DPI = 96)
  • 140% (DPI = 134.4)
  • 180% (DPI = 172.8)