模糊 XAML 矢量图形
Blurry XAML vector graphics
为了使我的 WPF 应用程序具有高 DPI 感知能力,我选择使用嵌入式 XAML 矢量图形创建所有内部图形和图标。
但是,我不明白为什么我的 XAML 矢量图在渲染时很模糊。这是一个例子。我使用 XAML 路径和矩形元素绘制了一个简单的软盘图标。下图是同一图标以两种不同的大小呈现,并具有不同的 SnapsToDevicePixels
和 UseLayoutRounding
值(这是在 Windows 7 中我显示器的原始分辨率 2048x1152)。
鉴于图标主要是水平和垂直线,所以看起来奇怪地模糊。这是较大图标的 600% 缩放视图:
以及较小的图标:
两种尺寸的边缘都不清晰。并且随着 SnapsToDevicePixels
的不同值,一些边缘是清晰的,但其他边缘仍然不是。
绘制这样的图标才不会模糊不清的正确方法是什么?
这里是这个window的XAML,包括路径数据:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XAML Path Test" Height="175" Width="525">
<Window.Resources>
<ResourceDictionary>
<Canvas x:Key="icon">
<Path Width="84" Height="83" Canvas.Left="6.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF" Data="F1 M 9,12L 68.9914,12L 88,31.0086L 88,90L 9,90L 9,12 Z "/>
<Rectangle Width="43" Height="41" Canvas.Left="23.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FF000000"/>
<Rectangle Width="23" Height="31" Canvas.Left="38.5" Canvas.Top="11.25" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF"/>
</Canvas>
</ResourceDictionary>
</Window.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Width="50" Height="50" Margin="10">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="50" Height="50" Margin="10" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="50" Height="50" Margin="10" UseLayoutRounding="True" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Width="24" Height="24" Margin="10">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="24" Height="24" Margin="10" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="24" Height="24" Margin="10" UseLayoutRounding="True" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</StackPanel>
</Window>
编辑:
按照 Chris W. 的建议将路径+矩形包裹在 ControlTemplate 中,并使用 Viewbox 调整大小,图像仍然模糊。从左到右,这是未调整大小(canvas 大小为 100x100)、调整大小为 50 和调整大小为 24:
以及 600% 缩放视图:
这是 ControlTemplate
:
<ControlTemplate x:Key="icon">
<Canvas Width="100" Height="100">
<Path Width="84" Height="83" Canvas.Left="6.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF" Data="F1 M 9,12L 68.9914,12L 88,31.0086L 88,90L 9,90L 9,12 Z "/>
<Rectangle Width="43" Height="41" Canvas.Left="23.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FF000000"/>
<Rectangle Width="23" Height="31" Canvas.Left="38.5" Canvas.Top="11.25" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF"/>
</Canvas>
</ControlTemplate>
以及用法:
<Control Template="{StaticResource icon}"/>
<Viewbox Height="50">
<Control Template="{StaticResource icon}"/>
</Viewbox>
<Viewbox Height="24">
<Control Template="{StaticResource icon}"/>
</Viewbox>
您的问题是您正在使用 VisualBrush
在其中绘制一个区域并且您失去了实际矢量状态。
有很多 other ways 可以根据您的实际需要和您打算用它做什么来完成这个,但是对于您的特定实例,仅根据我所看到的,我会把它切换成这样的而不是只使用 ControlTemplate
<Window>
<Window.Resources>
<ControlTemplate x:Key="icon">
<Canvas>
<Path Width="84" Height="83" Canvas.Left="6.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF" Data="F1 M 9,12L 68.9914,12L 88,31.0086L 88,90L 9,90L 9,12 Z "/>
<Rectangle Width="43" Height="41" Canvas.Left="23.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FF000000"/>
<Rectangle Width="23" Height="31" Canvas.Left="38.5" Canvas.Top="11.25" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF"/>
</Canvas>
</ControlTemplate>
</Window.Resources>
<Grid>
<Control Template="{StaticResource icon}"/>
</Grid>
</Window>
希望这对您有所帮助,祝您周末愉快! :)
On 和 PS - 要调整大小,请牢记 ViewBox
否则您将需要重新设置路径以使其不被硬设置。如果是我,我会在一条路径中创建一个像这样的连接图标(如果您使用混合,select 路径和矩形并右键单击 -> 组合 -> 联合)为一条路径数据,然后将其转换为 Style 模板,并放弃 ViewBox 和其他内容。
附加信息:
您也可以像这样模板化路径,这将是真正的向量;
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Path" x:Key="DiskIcon">
<Setter Property="Data" Value="F1 M 88.813,81.850 L 2.000,81.850 L 2.000,2.000 L 17.070,2.000 L 17.070,35.930 L 63.417,35.930 L 63.417,2.299 L 88.813,27.379 L 88.813,81.850 Z M 55.800,2.825 L 55.800,26.341 L 39.818,26.341 L 39.818,2.825 L 55.800,2.825 Z M 63.935,0.000 L 0.000,0.000 L 0.000,83.850 L 90.813,83.850 L 90.813,26.543 L 63.935,0.000 Z"/>
<Setter Property="Fill" Value="Black"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Width" Value="100"/>
</Style>
</StackPanel.Resources>
<Path Style="{StaticResource DiskIcon}"/>
<Path Style="{StaticResource DiskIcon}" Fill="Red"/>
<Path Style="{StaticResource DiskIcon}" Fill="Blue"/>
</StackPanel>
根据情况和需要,尽管我经常将其构建到 ContentControl
中以托管嵌入式 CLR ViewBox
,如 "other ways" link 中所述,我在开头喜欢;
<StackPanel>
<StackPanel.Resources>
<Style x:Key="TheAwesomeXAMLimage" TargetType="ContentControl">
<Setter Property="Background" Value="Black"/>
<Setter Property="Height" Value="90"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Viewbox Stretch="Fill">
<Path Fill="{TemplateBinding Background}"
Data="F1 M 88.813,81.850 L 2.000,81.850 L 2.000,2.000 L 17.070,2.000 L 17.070,35.930 L 63.417,35.930 L 63.417,2.299 L 88.813,27.379 L 88.813,81.850 Z M 55.800,2.825 L 55.800,26.341 L 39.818,26.341 L 39.818,2.825 L 55.800,2.825 Z M 63.935,0.000 L 0.000,0.000 L 0.000,83.850 L 90.813,83.850 L 90.813,26.543 L 63.935,0.000 Z"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ContentControl Style="{StaticResource TheAwesomeXAMLimage}"/>
<ContentControl Style="{StaticResource TheAwesomeXAMLimage}"
Background="Red" Height="50" Width="60" Margin="10"/>
</StackPanel>
为了使我的 WPF 应用程序具有高 DPI 感知能力,我选择使用嵌入式 XAML 矢量图形创建所有内部图形和图标。
但是,我不明白为什么我的 XAML 矢量图在渲染时很模糊。这是一个例子。我使用 XAML 路径和矩形元素绘制了一个简单的软盘图标。下图是同一图标以两种不同的大小呈现,并具有不同的 SnapsToDevicePixels
和 UseLayoutRounding
值(这是在 Windows 7 中我显示器的原始分辨率 2048x1152)。
鉴于图标主要是水平和垂直线,所以看起来奇怪地模糊。这是较大图标的 600% 缩放视图:
以及较小的图标:
两种尺寸的边缘都不清晰。并且随着 SnapsToDevicePixels
的不同值,一些边缘是清晰的,但其他边缘仍然不是。
绘制这样的图标才不会模糊不清的正确方法是什么?
这里是这个window的XAML,包括路径数据:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="XAML Path Test" Height="175" Width="525">
<Window.Resources>
<ResourceDictionary>
<Canvas x:Key="icon">
<Path Width="84" Height="83" Canvas.Left="6.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF" Data="F1 M 9,12L 68.9914,12L 88,31.0086L 88,90L 9,90L 9,12 Z "/>
<Rectangle Width="43" Height="41" Canvas.Left="23.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FF000000"/>
<Rectangle Width="23" Height="31" Canvas.Left="38.5" Canvas.Top="11.25" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF"/>
</Canvas>
</ResourceDictionary>
</Window.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Width="50" Height="50" Margin="10">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="50" Height="50" Margin="10" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="50" Height="50" Margin="10" UseLayoutRounding="True" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Width="24" Height="24" Margin="10">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="24" Height="24" Margin="10" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
<Rectangle Width="24" Height="24" Margin="10" UseLayoutRounding="True" SnapsToDevicePixels="True">
<Rectangle.Fill>
<VisualBrush Visual="{StaticResource icon}"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</StackPanel>
</Window>
编辑:
按照 Chris W. 的建议将路径+矩形包裹在 ControlTemplate 中,并使用 Viewbox 调整大小,图像仍然模糊。从左到右,这是未调整大小(canvas 大小为 100x100)、调整大小为 50 和调整大小为 24:
以及 600% 缩放视图:
这是 ControlTemplate
:
<ControlTemplate x:Key="icon">
<Canvas Width="100" Height="100">
<Path Width="84" Height="83" Canvas.Left="6.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF" Data="F1 M 9,12L 68.9914,12L 88,31.0086L 88,90L 9,90L 9,12 Z "/>
<Rectangle Width="43" Height="41" Canvas.Left="23.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FF000000"/>
<Rectangle Width="23" Height="31" Canvas.Left="38.5" Canvas.Top="11.25" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF"/>
</Canvas>
</ControlTemplate>
以及用法:
<Control Template="{StaticResource icon}"/>
<Viewbox Height="50">
<Control Template="{StaticResource icon}"/>
</Viewbox>
<Viewbox Height="24">
<Control Template="{StaticResource icon}"/>
</Viewbox>
您的问题是您正在使用 VisualBrush
在其中绘制一个区域并且您失去了实际矢量状态。
有很多 other ways 可以根据您的实际需要和您打算用它做什么来完成这个,但是对于您的特定实例,仅根据我所看到的,我会把它切换成这样的而不是只使用 ControlTemplate
<Window>
<Window.Resources>
<ControlTemplate x:Key="icon">
<Canvas>
<Path Width="84" Height="83" Canvas.Left="6.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF" Data="F1 M 9,12L 68.9914,12L 88,31.0086L 88,90L 9,90L 9,12 Z "/>
<Rectangle Width="43" Height="41" Canvas.Left="23.5" Canvas.Top="9.5" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FF000000"/>
<Rectangle Width="23" Height="31" Canvas.Left="38.5" Canvas.Top="11.25" Stretch="Fill" StrokeThickness="5" StrokeMiterLimit="2.75" Stroke="#FF000000" Fill="#FFFFFFFF"/>
</Canvas>
</ControlTemplate>
</Window.Resources>
<Grid>
<Control Template="{StaticResource icon}"/>
</Grid>
</Window>
希望这对您有所帮助,祝您周末愉快! :)
On 和 PS - 要调整大小,请牢记 ViewBox
否则您将需要重新设置路径以使其不被硬设置。如果是我,我会在一条路径中创建一个像这样的连接图标(如果您使用混合,select 路径和矩形并右键单击 -> 组合 -> 联合)为一条路径数据,然后将其转换为 Style 模板,并放弃 ViewBox 和其他内容。
附加信息:
您也可以像这样模板化路径,这将是真正的向量;
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Path" x:Key="DiskIcon">
<Setter Property="Data" Value="F1 M 88.813,81.850 L 2.000,81.850 L 2.000,2.000 L 17.070,2.000 L 17.070,35.930 L 63.417,35.930 L 63.417,2.299 L 88.813,27.379 L 88.813,81.850 Z M 55.800,2.825 L 55.800,26.341 L 39.818,26.341 L 39.818,2.825 L 55.800,2.825 Z M 63.935,0.000 L 0.000,0.000 L 0.000,83.850 L 90.813,83.850 L 90.813,26.543 L 63.935,0.000 Z"/>
<Setter Property="Fill" Value="Black"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Width" Value="100"/>
</Style>
</StackPanel.Resources>
<Path Style="{StaticResource DiskIcon}"/>
<Path Style="{StaticResource DiskIcon}" Fill="Red"/>
<Path Style="{StaticResource DiskIcon}" Fill="Blue"/>
</StackPanel>
根据情况和需要,尽管我经常将其构建到 ContentControl
中以托管嵌入式 CLR ViewBox
,如 "other ways" link 中所述,我在开头喜欢;
<StackPanel>
<StackPanel.Resources>
<Style x:Key="TheAwesomeXAMLimage" TargetType="ContentControl">
<Setter Property="Background" Value="Black"/>
<Setter Property="Height" Value="90"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Viewbox Stretch="Fill">
<Path Fill="{TemplateBinding Background}"
Data="F1 M 88.813,81.850 L 2.000,81.850 L 2.000,2.000 L 17.070,2.000 L 17.070,35.930 L 63.417,35.930 L 63.417,2.299 L 88.813,27.379 L 88.813,81.850 Z M 55.800,2.825 L 55.800,26.341 L 39.818,26.341 L 39.818,2.825 L 55.800,2.825 Z M 63.935,0.000 L 0.000,0.000 L 0.000,83.850 L 90.813,83.850 L 90.813,26.543 L 63.935,0.000 Z"/>
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ContentControl Style="{StaticResource TheAwesomeXAMLimage}"/>
<ContentControl Style="{StaticResource TheAwesomeXAMLimage}"
Background="Red" Height="50" Width="60" Margin="10"/>
</StackPanel>