/ UWP

Win2D 官方文章系列翻译 - 调整控件分辨率

本文旨在讲解如何配置 Win2D XAML 控件使用的分辨率。下文将介绍如何:

  • 使 Win2D 控件运行在固定的分辨率。
  • 通过调整控件 DPI 减少渲染像素数以增强性能。

分辨率与控件缩放

本文中所用的“分辨率”一词,是指位图的尺寸(高宽)。

Win2D XAML 控件绘制的对象均有分辨率和 DPI 这两项属性。对象的 DPI 指示了绘制时其构成像素的密度。 DPI 就像缩放因数 —— DPI 越高则绘制对象构成的像素数越多;反之,DPI 越低,其构成像素数就越少。有关一般 Win2D 对 DPI 处理的更多详情,可以参阅另一篇文章

DPI 无关尺寸有时也称作“逻辑尺寸”。而 DPI 相关尺寸,也就是以像素为单位的尺寸,也称作“物理尺寸”。

一个控件在加载时其分辨率和缩放的默认行为是:

  • 控件依据其布局以及其在 XAML 元素树中的位置决定其逻辑尺寸。
  • 控件依据系统运行环境的 DPI 值决定其自身 DPI。
  • 控件依据经 DPI 缩放后的控件尺寸决定其可绘区域的物理像素数。
  • 高 DPI 情况下,物理尺寸会大于逻辑尺寸(像素更多)。
  • 低 DPI 情况下,物理尺寸会小于逻辑尺寸(像素更少)。
  • 默认 DPI 情况下,可绘区域的物理尺寸会等于逻辑尺寸。
  • 控件将其绘制资源(CanvasControl 控件的 CanvasImageSource, CanvasVirtualControl 控件的 CanvasVirtualImageSourceCanvasAnimatedControl 控件的 CanvasSwapChain)的尺寸和 DPI 设置为与自身匹配。

大部分 Win2D 操作都是以 DIPs (DPI 无关的单位)为基础进行的,并且 Win2D XAML 控件的绘图资源会自动依据 DPI 进行缩放。这意味着通常应用可以忽略 DPI。除非额外指定,尺寸和坐标总是 DPI 无关的。一个应用可以为绘制到控件的内容硬编码多种不同的尺寸和坐标,当应用运行在不同 DPI 的环境下时,内容就会随之进行缩放。

但对某些应用而言,这种默认的行为并不足够。本文概括了几种默认行为不足以应对的场景,并讲解了应用能如何对其进行修正。

场景:控件内容的分辨率必须固定且小于正常值

当一个 2D 精灵(sprite) 游戏需要无视显示硬件的实际分辨率二总是以固定的 640x480 分辨率进行渲染时,要求的正是这种场景。

要解决这一需求完全无需额外编写任何 Win2D 代码。

XAML 控件 ViewBox 能够强制其子元素的尺寸变化,通过应用信箱模式(在内容上下添加边距)或邮筒模式(在内容两侧添加边距)在保持高宽比的情况下自动进行缩放。

仅需确保你的 CanvasControlCanvasVirtualControlCanvasAnimatedControl 控件被设置为一个 ViewBox 控件的子元素,尺寸受其限制。

例如,要无视 DPI 而强制把控件的宽设为 320 像素,高设为 224 像素,则将

<canvas:CanvasAnimatedControl/>

修改为

<Viewbox>
    <canvas:CanvasAnimatedControl  Width="320" Height="224"/>
</Viewbox>

如果你不希望应用通过使用信箱模式/邮筒模式保持内容比例,则需要添加 Stretch 属性:

<Viewbox Stretch="Fill">
    <canvas:CanvasAnimatedControl Width="320" Height="224"/>
</Viewbox>

注意使用 Viewbox 对控件进行缩放时其插值模式并不能保证缩放效果。其过滤方法可能看起来就像于 CanvasInterpolationMode.Linear 或其它类似方法。如果你的一个用需要某种特定的插值模式,那么就不要使用 ViewBox 固定控件尺寸,而应当将内容绘制到一个固定尺寸的 CanvasRenderTarget 作为中转,再以希望的插值模式缩放绘制到目标。

场景:应用在高分辨率环境下渲染失常

某些显示设备拥有极高的分辨率,但其 GPU (图形处理单元)的性能并不足以流畅处理大量像素的动画。若不经测试,开发者很难知道应用在此类高分辨率设备上表现如何。一种解决方案是利用控件的 DpiScale 属性减小控件的 DPI。

例如,使用以下代码将控件的分辨率减半:

<canvas:CanvasAnimatedControl DpiScale="0.5f" />

实际使用中,DPI 缩放因数取决于你应用的具体需求。一种做法是固定缩放因数,确保应用的 DPI 总是 96而非更高。

例如:

float dpiLimit = 96.0f;
if(control.Dpi > dpiLimit)
{
    control.DpiScale = dpiLimit / control.Dpi;
}

为了确保这一做法在 DPI 发生改变时能够正常生效,应用需订阅 DisplayInformation.DpiChanged 事件,在时间处理逻辑中以变更后的 DPI 计算并设置 DPI 缩放因数。

这种做法通过利用在高 DPI 环境下不易察觉分辨率缩小的情况换取了部分性能上的提升。

类似于上文提到的 ViewBox,缩小控件分辨率这一方法的插值模式也不能保证控件的缩放效果。如果你的应用需要某种特定的插值模式,还是需要转而使用一个绘图中间件。