在 WPF 应用程序中进行像素对齐

更新:2007 年 11 月

WPF 图形系统使用与设备无关的单元来支持分辨率和设备无关性。每个与设备无关的像素都会随系统上的每英寸点数 (dpi) 设置自动缩放。这支持 WPF 应用程序可以根据不同的 dpi 设置进行适当的缩放,并使应用程序可以自动识别 dpi。

但是,这种 dpi 无关性可能会因为消除锯齿而呈现出不规则的边缘。当边缘的位置处于设备像素内而不是位于设备像素之间时,可出现这些通常显示为模糊边缘或半透明的效果边缘。为解决此问题,WPF 提供了一种通过像素对齐将可视化树中的对象边缘与设备像素对齐或固定到设备像素的方法,从而消除了因消除锯齿而产生的半透明边缘。

像素对齐是一种通过对可见几何图形应用较小的偏移量以将几何图形与设备像素对齐、从而消除视觉伪影效果的方法。

本主题包括下列各节。

  • 针对消除锯齿的呈现进行像素对齐
  • 参考线
  • 位图图像
  • 相关主题

针对消除锯齿的呈现进行像素对齐

锐化线条

如果不使用像素对齐,则消除锯齿呈现的线条可能会显示为半透明(如果边缘未处于设备像素之间)。下面的插图演示一条宽度为一像素、位于设备像素内的已消除锯齿的线条(左侧)和一条宽度为一像素、位于设备像素之间的线条(右侧)。

消除锯齿的线条的呈现。

消除锯齿的线与单像素线的比较。

使用像素对齐,已消除锯齿的线条将与设备像素对齐或固定到设备像素而且显示效果的锐度很好,消除了半透明线条的呈现效果。以下示例演示 SnapsToDevicePixels 属性对单个像素的线条的影响。缓慢地调整窗口大小,随着线条位置的更改,将显示未对齐线条(左)上的视觉效果和已对齐线条的固定大小(右)。

<Page x:Class="PixelSnapping.Lines"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Lines" Name="linesPage"
    >
  <StackPanel Width="150"  Margin="7" Orientation="Horizontal">
    <!-- Single pixel line with pixel snapping turned OFF.-->
    <Rectangle SnapsToDevicePixels="False"
       Width="45.5" Margin="10" Height="1" Fill="Red"/>
    <!-- Single pixel line with pixel snapping turned ON.-->
    <Rectangle SnapsToDevicePixels="True"
      Width="45.5" Margin="10" Height="1" Fill="Red"/>
  </StackPanel>
  <!-- Background Grid -->
  <Page.Background>
    <DrawingBrush  Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile">
      <DrawingBrush.Drawing>
        <DrawingGroup>
          <GeometryDrawing Brush="White">
            <GeometryDrawing.Geometry>
              <RectangleGeometry Rect="0,0,1,1" />
            </GeometryDrawing.Geometry>
          </GeometryDrawing>
          <GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
          <GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
        </DrawingGroup>
      </DrawingBrush.Drawing>
    </DrawingBrush>
  </Page.Background>
</Page>
说明:

SnapsToDevicePixels 仅影响在布局处理过程中运行的元素。可以使用 DrawingGroup 对象的 GuidelineSet 属性在 Drawing 上设置参考线。若要手动设置 Visual 的参考线,请使用 VisualYSnappingGuidelinesVisualXSnappingGuidelines 属性创建新的参考线。

像素对齐仅锐化水平线和垂直线,不影响对角线。

紧邻的对象

如果对象间的边缘靠紧在一起并且相连的边缘未在一行或一列设备像素间准确对齐,则消除锯齿也会产生视觉效果。这种消除锯齿现象可以使用底层背景颜色使边缘柔和,从而产生渗透效果,在这种效果中两个对象之间的边缘均显示为透明的颜色。下图演示了这种渗透效果。

呈现渗透效果的邻接对象。

在邻接的对象之间渗出的背景

如果启用了像素对齐,则将邻接的边缘与设备像素对齐以消除渗透效果。下面的示例演示 SnapsToDevicePixels 属性对邻接对象的作用。缓慢地调整窗口的大小,您将看到未对齐矩形(左)上的渗透问题,而对齐的矩形(右)则保持在一起,没有视觉上的不规则现象。

<Page x:Class="PixelSnapping.Seeping"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Seeping"
    >
  <StackPanel Orientation="Horizontal" Height="100">
    <Border  
      SnapsToDevicePixels="False"
      Margin="10" BorderThickness="1" BorderBrush="Black" Height="80" Background="White">
      <StackPanel Height="100.0">
        <Rectangle Width="20" Height="20" Fill="Red"/>
        <Rectangle Width="20" Height="20" Fill="Red"/>
        <Rectangle Width="20" Height="20" Fill="Red"/>
        <Rectangle Width="20" Height="20" Fill="Red"/>
      </StackPanel>
    </Border>
    <Border
      SnapsToDevicePixels="True"
        Margin="10" BorderThickness="1" BorderBrush="Black" Height="80" Background="White">
      <StackPanel Height="100.0">
        <Rectangle Width="20" Height="20" Fill="Red"/>
        <Rectangle Width="20" Height="20" Fill="Red"/>
        <Rectangle Width="20" Height="20" Fill="Red"/>
        <Rectangle Width="20" Height="20" Fill="Red"/>
      </StackPanel>
    </Border>
  </StackPanel>
  <!-- Background Grid -->
  <Page.Background>
    <DrawingBrush  Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile">
      <DrawingBrush.Drawing>
        <DrawingGroup>
          <GeometryDrawing Brush="White">
            <GeometryDrawing.Geometry>
              <RectangleGeometry Rect="0,0,1,1" />
            </GeometryDrawing.Geometry>
          </GeometryDrawing>
          <GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
          <GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
        </DrawingGroup>
      </DrawingBrush.Drawing>
    </DrawingBrush>
  </Page.Background>
</Page>

请注意,对齐的矩形未明确设置 SnapsToDevicePixels 属性的值。只需在根处将该属性设置为 true,即可在所有子元素上启用该行为。

Text

Windows Presentation Foundation (WPF) 始终生成消除锯齿的文本,如果该文本是静态的,则将该文本进行像素对齐。这通过将标志符号直接放置在像素网格上来帮助对消除锯齿的文本进行锐化,从而提供更为清晰的文本。但是,当 Windows Presentation Foundation (WPF) 检测到任何类似动画的动作(例如,滚动、缩放和动画平移)时,将关闭像素对齐,直到该动作结束。一旦该动画或滚动结束,像素对齐将重新缓慢地以动画形式进行。

参考线

本质上而言,像素对齐是由参考线控制的。在根据设备像素网格调整几何图形时,参考线很有用。在大多数情况下,使用 SnapsToDevicePixels 属性的像素对齐可提供所需的结果。然而,该属性并不是始终可用,尤其是在使用 Drawing 对象或使用 DrawingContext 直接进行处理时更是如此,因此必须设置参考线以实现像素对齐提供的所需锐度。

若要在 DrawingDrawingContext 对象上设置参考线,应使用 GuidelineSet 类。可以使用此类创建水平准线或垂直参考线,这些准线可应用于 DrawingGroup 或被推送到 DrawingContext,以供后面的绘图命令使用。参考线指示绘图哪些线条应该与设备像素对齐。有关 GuidelineSet 用法的详细示例,请参见如何:向绘图应用 GuidelineSet

还可以通过更改水平和垂直参考线集合在 Visual 级别设置参考线。可以通过 VisualYSnappingGuidelinesVisualXSnappingGuidelines 属性访问这些参考线。

位图图像

由于 WPF 的 dpi 无关性,基于位图的 UI 可以产生意想不到的表现效果。消除锯齿现象可能会因小数像素排列问题使图像模糊。对于包含高频率变化(例如紧邻反差大的元素的单个像素线条,类似黑白相间的线条)的图像,尤其如此。下图演示了对齐的图像(左)和已偏移从而不与设备像素对齐的图像(右)之间的图像质量差异。

图像与设备像素对齐。

由于设备像素未对齐而模糊的图像。

UI 中图像的一个常见情形是将表示另一对象中的图标的图像移至中心。由于图标通常是变化频率较高的小图像,因此需要对应用程序的布局进行调整,以避免因消除锯齿而产生的视觉效果。

为了准确地将图像置于中心,如果图像像素的宽度、高度为偶数,则容器的宽度、高度的像素数也应为偶数。如果图像的宽度、高度的像素数为奇数,则容器元素的宽度、高度的像素数也应该为奇数。

下面的示例创建两个包含 ImageBorder 对象。上边框的宽度和高度值都为偶数,以匹配图像的宽度和高度值。下边框的宽度和高度值均为奇数。

下图演示了该示例的输出结果和容器大小对图像的影响。

在边框内居中的图像。

<Page x:Class="PixelSnapping.Images"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Images"
    >
  <StackPanel>
    <!-- Image has a pixel dimension of 144x96. -->
    <!-- Because the image has a even width and height, 
         an even border width and height allows the image to proper center. 
    -->
    <Border HorizontalAlignment="Left" VerticalAlignment="Top" Width="200" Height="100">
      <Image HorizontalAlignment="Center" VerticalAlignment="Center" Source="sharpness.png" Stretch="None"/>
    </Border>
    <!-- Image has a pixel dimension of 144x96. -->
    <!-- Because the image has a even width and height, 
         an odd border width and height causes the image to soften. 
    -->
    <Border HorizontalAlignment="Left" VerticalAlignment="Top" Width="201" Height="101">
      <Image HorizontalAlignment="Center" VerticalAlignment="Center" Source="sharpness.png" Stretch="None"/>
    </Border>
  </StackPanel>
  <!-- Grid Background -->
  <Page.Background>
    <DrawingBrush  Viewport="0,0,10,10" ViewportUnits="Absolute" TileMode="Tile">
      <DrawingBrush.Drawing>
        <DrawingGroup>
          <GeometryDrawing Brush="White">
            <GeometryDrawing.Geometry>
              <RectangleGeometry Rect="0,0,1,1" />
            </GeometryDrawing.Geometry>
          </GeometryDrawing>
          <GeometryDrawing Geometry="M0,0 L1,0 1,0.1, 0,0.1Z " Brush="#CCCCFF" />
          <GeometryDrawing Geometry="M0,0 L0,1 0.1,1, 0.1,0Z" Brush="#CCCCFF" />
        </DrawingGroup>
      </DrawingBrush.Drawing>
    </DrawingBrush>
  </Page.Background>
</Page>

遗憾的是,仅调整容器对象的大小并不能保证设备像素的对齐。应用程序的整个布局可以对图像的对齐产生影响。显示每英寸点数 (dpi) 也会影响图像的对齐。在以上示例中,仅在将显示设置为 96 每英寸点数 (dpi) 时,图像对齐才起作用。在其他设置中,需要调整布局以适应显示设置。在 Windows Presentation Foundation (WPF) 应用程序中,应尽量避免高频图像。

请参见

概念

布局系统

参考

Image

VisualXSnappingGuidelines

VisualYSnappingGuidelines