繁体   English   中英

如何使用C#反转XAML PNG图像的颜色?

[英]How to Invert Color of XAML PNG Images using C#?

我正在使用Visual Studio,C#,XAML,WPF。

在我的程序中,我有带有白色png图标的XAML按钮。

在此输入图像描述

我想拥有它,所以你可以通过从ComboBox中选择主题来切换到带有黑色图标的主题。

不是创建一组新的黑色png图像,有没有办法使用XAML和C#我可以反转白色图标的颜色?

<Button x:Name="btnInfo" HorizontalAlignment="Left" Margin="10,233,0,0" VerticalAlignment="Top" Width="22" Height="22" Cursor="Hand" Click="buttonInfo_Click" Style="{DynamicResource ButtonSmall}">
    <Image Source="Resources/Images/info.png" Width="5" Height="10" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0,0,0"/>
</Button>

谢谢你提出这个问题。 它给了我一个学习新东西的机会。 :)

你的目标是,一旦你知道自己在做什么,就很容易实现。 WPF支持使用GPU着色器修改图像。 它们在运行时很快(因为它们在您的视频卡中执行)并且易于应用。 并且在所述目标反转颜色的情况下,也非常容易实现。

首先,您需要着色器代码。 着色器使用称为高级着色器语言 (HLSL)的语言编写。 这是一个HLSL“程序”,它将反转输入颜色:

sampler2D input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 color = tex2D(input, uv);
    float alpha = color.a;

    color = 1 - color;
    color.a = alpha;
    color.rgb *= alpha;

    return color;
}

但是,Visual Studio不直接处理这种代码。 您需要确保安装了DirectX SDK,它将为您提供用于编译着色器代码的fxc.exe编译器。

我用这个命令行编译了上面的代码:

fxc /T ps_3_0 /E main /Fo<my shader file>.ps <my shader file>.hlsl

当然,您将<my shader file>替换为您的实际文件名。

(注意:我手动执行此操作,但您当然可以在项目中创建自定义构建操作以执行相同操作。)

然后,您可以在项目中包含.ps文件,将“Build Action”设置“Resource”

完成后,您现在需要创建将使用它的ShaderEffect类。 看起来像这样:

class InvertEffect : ShaderEffect
{
    private static readonly PixelShader _shader =
        new PixelShader { UriSource = new Uri("pack://application:,,,/<my shader file>.ps") };

    public InvertEffect()
    {
        PixelShader = _shader;
        UpdateShaderValue(InputProperty);
    }

    public Brush Input
    {
        get { return (Brush)GetValue(InputProperty); }
        set { SetValue(InputProperty, value); }
    }

    public static readonly DependencyProperty InputProperty =
        ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(InvertEffect), 0);

}

以上代码的要点:

  • 您只需要一个着色器本身的副本。 所以我将其初始化为static readonly字段。 由于.ps文件作为资源包含在内,我可以使用pack: scheme来引用它,如"pack://application:,,,/<my shader file>.ps" 同样,您需要将<my shader file>替换为实际的文件名。
  • 在构造函数中, 必须PixelShader属性设置为着色器对象。 您还必须调用UpdateShaderValue()来初始化着色器,对于用作着色器输入的每个属性(在这种情况下,只有一个)。
  • Input属性是特殊的:它需要使用RegisterPixelShaderSamplerProperty()来注册依赖项属性。
  • 如果着色器具有其他参数,则通常使用DependencyProperty.Register()注册它们。 但是它们需要一个特殊的PropertyChangedCallback值,通过调用ShaderEffect.PixelShaderConstantCallback()获得该参数的着色器代码中声明的寄存器索引。

这里的所有都是它的!

只需将UIElement.Effect属性设置为InvertEffect类的实例,就可以在XAML中使用上述内容。 例如:

<Window x:Class="TestSO45093399PixelShader.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:l="clr-namespace:TestSO45093399PixelShader"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Rectangle Width="100" Height="100">
      <Rectangle.Fill>
        <LinearGradientBrush>
          <GradientStop Color="Black" Offset="0"/>
          <GradientStop Color="White" Offset="1"/>
        </LinearGradientBrush>
      </Rectangle.Fill>
      <Rectangle.Effect>
        <l:InvertEffect/>
      </Rectangle.Effect>
    </Rectangle>
  </Grid>
</Window>

当你运行它时,你会注意到即使渐变在左下角转换为白色的左上角定义为黑色,它也会以相反的方式显示,左上角为白色,下半部分为黑色。 -对。

最后,关于偶然的机会你想立即让它工作,并且无法访问fxc.exe编译器,这里是上面的一个版本,它具有嵌入Base64的编译着色器代码。 它很小,所以这是编译和包含着色器作为资源的实用替代方法。

class InvertEffect : ShaderEffect
{
    private const string _kshaderAsBase64 =
@"AAP///7/HwBDVEFCHAAAAE8AAAAAA///AQAAABwAAAAAAQAASAAAADAAAAADAAAAAQACADgAAAAA
AAAAaW5wdXQAq6sEAAwAAQABAAEAAAAAAAAAcHNfM18wAE1pY3Jvc29mdCAoUikgSExTTCBTaGFk
ZXIgQ29tcGlsZXIgMTAuMQCrUQAABQAAD6AAAIA/AAAAAAAAAAAAAAAAHwAAAgUAAIAAAAOQHwAA
AgAAAJAACA+gQgAAAwAAD4AAAOSQAAjkoAIAAAMAAAeAAADkgQAAAKAFAAADAAgHgAAA/4AAAOSA
AQAAAgAICIAAAP+A//8AAA==";

    private static readonly PixelShader _shader;

    static InvertEffect()
    {
        _shader = new PixelShader();
        _shader.SetStreamSource(new MemoryStream(Convert.FromBase64String(_kshaderAsBase64)));
    }

    public InvertEffect()
    {
        PixelShader = _shader;
        UpdateShaderValue(InputProperty);
    }

    public Brush Input
    {
        get { return (Brush)GetValue(InputProperty); }
        set { SetValue(InputProperty, value); }
    }

    public static readonly DependencyProperty InputProperty =
        ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(InvertEffect), 0);

}

最后,我会注意到Bradley评论中提供的链接确实包含了大量这些着色器实现的效果。 那些实现了HLSL和ShaderEffect对象的作者与我在这里展示的方式略有不同,所以如果你想看到其他效果的例子和实现它们的不同方法,那么浏览这些代码将是一个很好看的地方。

请享用!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM