【问题标题】:Blurry XAML vector graphics模糊的 XAML 矢量图形
【发布时间】:2016-11-11 11:53:45
【问题描述】:

为了使我的 WPF 应用程序具有高 DPI 感知能力,我选择使用嵌入式 XAML 矢量图形创建所有内部图形和图标。

但是,我不明白为什么我的 XAML 矢量图形在呈现时是模糊的。这是一个例子。我使用 XAML Path 和 Rectangle 元素绘制了一个简单的软盘图标。下图是以两种不同尺寸呈现的同一个图标,并具有不同的 SnapsToDevicePixels 和 UseLayoutRounding 值(这是我的显示器在 Windows 7 中的原始分辨率为 2048x1152)。

鉴于图标大多是水平和垂直线,它看起来奇怪的模糊。这是较大图标的 600% 缩放视图:

还有更小的图标:

两种尺寸的边缘都不脆。随着SnapsToDevicePixels 的值变化,一些边缘清晰,但其他边缘仍然不清晰。

绘制这样的图标以使它们不会模糊呈现的正确方法是什么?

这是此窗口的 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 调整大小,图像仍然模糊。从左到右,这是未调整大小(画布大小为 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>

【问题讨论】:

  • 我们做了类似的事情,但在我们的例子中,我的一位同事使用 SVG 制作图形,然后将其转换为 XAML 矢量图形。我不知道他是怎么做到的,他是我们团队的 UI 专家。我只是提出这个建议,你也许可以做同样的事情。
  • @Rod 他可能只是在 illustrator 中制作了它们,并使用了 Mike Swanson 的超级方便的花花公子 AI to XAML 插件,或者我认为 Inkscape 也可以导出到 Path XAML。
  • @ChrisW。现在我在 Expression Design 4 中绘制它们并使用复制到 XAML 功能。将来我想在 SVG 中绘制它们并从那里转换为 XAML。

标签: wpf xaml user-interface icons vector-graphics


【解决方案1】:

您的问题是您正在使用VisualBrush,其中您正在绘制一个区域并且您失去了您的实际矢量状态。

有很多 other ways 可以根据您的实际需求和计划使用它来完成此操作,但对于您的特定实例,仅基于我所看到的,我会将其切换为类似的内容,而不是仅使用 @ 987654324@

<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>

希望这对您有所帮助,祝您周末愉快! :)

开启和 PS - 调整大小时,请牢记ViewBox,否则您需要重新调整路径以免难以设置。如果是我,我会在一条路径中制作一个类似的连接图标(如果您使用的是混合,请选择路径和矩形并右键单击 -> 组合 -> 联合)用于单行路径数据,然后转改为样式模板并放弃 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,就像我在开头添加的“其他方式”链接中提到的那样;

<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>

【讨论】:

  • @proc-self-maps 其实我很抱歉,我遗漏了一些步骤,以防你想把它变成一个单一的路径并将它转换成一个快速的样式模板。如果您想尝试这条路线,请告诉我,我很快就会在这里详细介绍。
  • 嗯。我刚刚尝试过,并通过将&lt;Control&gt; 元素包装在 Viewbox 中来调整大小,但渲染的图像仍然模糊。我已经用新的屏幕截图编辑了我的问题。
  • @proc-self-maps 所以两件事。一,我稍后会进来把你的图像转换成一个单一的路径,当我有空的时候,我们可以试试它作为样式模板,看看你是否得到相同的结果。第二,当您说缩放时,是缩放还是缩放,或者您为这些结果做了什么?在某些时候,屏幕上的任何东西都会显示为具有锯齿(甚至矢量)的逐像素图像,因为它仍然是逐像素的。所以我有点想知道您所展示的用法是否可能超出预期?
  • 缩放视图是正在运行的应用程序的放大屏幕截图(希望这是有道理的)。我同意最终所有东西都必须光栅化为像素,但我不明白为什么调整大小的图标在直边上有锯齿。如果您在其他应用程序(例如 Visual Studio)上放大(例如,使用 Windows 中的放大工具),它们的图标没有这些锯齿伪影。我的图标中的锯齿伪影是肉眼可见的:边缘似乎有阴影或模糊,这是我试图消除的。
  • @proc-self-maps 不完全是。我曾经也这么认为,直到我被一些经验丰富的人(如this answer over on Graphics exchange)所谦卑。但是,我们将看看我们能想出什么。我刚刚遇到了一个忙碌的星期一,但我并没有忘记这一点,我会尽快回来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 2018-11-08
  • 2019-06-10
  • 2016-06-15
  • 1970-01-01
相关资源
最近更新 更多