本文为个人博客备份文章,原文地址:
http://validvoid.net/win2d-choosing-control-resolution/
本文旨在讲解如何配置 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
控件的CanvasVirtualImageSource
和CanvasAnimatedControl
控件的CanvasSwapChain
)的尺寸和 DPI 设置为与自身匹配。
大部分 Win2D 操作都是以 DIPs (DPI 无关的单位)为基础进行的,并且 Win2D XAML 控件的绘图资源会自动依据 DPI 进行缩放。这意味着通常应用可以忽略 DPI。除非额外指定,尺寸和坐标总是 DPI 无关的。一个应用可以为绘制到控件的内容硬编码多种不同的尺寸和坐标,当应用运行在不同 DPI 的环境下时,内容就会随之进行缩放。
但对某些应用而言,这种默认的行为并不足够。本文概括了几种默认行为不足以应对的场景,并讲解了应用能如何对其进行修正。
场景:控件内容的分辨率必须固定且小于正常值
当一个 2D 精灵(sprite) 游戏需要无视显示硬件的实际分辨率二总是以固定的 640x480 分辨率进行渲染时,要求的正是这种场景。
要解决这一需求完全无需额外编写任何 Win2D 代码。
XAML 控件 ViewBox 能够强制其子元素的尺寸变化,通过应用信箱模式(在内容上下添加边距)或邮筒模式(在内容两侧添加边距)在保持高宽比的情况下自动进行缩放。
仅需确保你的 CanvasControl
、CanvasVirtualControl
或 CanvasAnimatedControl
控件被设置为一个 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,缩小控件分辨率这一方法的插值模式也不能保证控件的缩放效果。如果你的应用需要某种特定的插值模式,还是需要转而使用一个绘图中间件。