如何使用C#转换XAML PNG图像的颜色? [英] How to Invert Color of XAML PNG Images using C#?

查看:292
本文介绍了如何使用C#转换XAML PNG图像的颜色?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using Visual Studio, C#, XAML, WPF.

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

In my program I have XAML buttons with white png icons.

我想要它,因此您可以通过从ComboBox中选择主题来切换到带有黑色图标的主题.

I want to have it so you can switch to a theme with black icons by choosing the theme from a ComboBox.

代替创建一组新的黑色png图像,XAML和C#是否可以将白色图标的颜色反转?

Instead of creating a new set of black png images, is there a way with XAML and C# I can invert the color of the white icons?

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

推荐答案

感谢此问题.它给了我学习新知识的机会. :)

Thanks for this question. It gave me a chance to learn something new. :)

您的目标是,一旦您知道自己在做什么,就很容易实现. WPF支持使用GPU着色器修改图像.它们运行时间快(因为它们在您的视频卡中执行)并且易于应用.而且,如果要实现既定目标的颜色反转,则也很容易实现.

Your goal is, once you know what you're doing, very easy to achieve. WPF supports the use of GPU shaders to modify images. They are fast at run-time (since they execute in your video card) and easy to apply. And in the case of the stated goal to invert the colors, very easy to implement as well.

首先,您需要着色器代码.着色器以高级着色器语言或HLSL语言编写.这是一个HLSL程序",它将反转输入颜色:

To start with, you'll need the shader code. Shaders are written in a language called High Level Shader Language, or HLSL. Here is an HLSL "program" that will invert the input color:

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编译器.

But, Visual Studio doesn't handle this kind of code directly. You'll need to make sure you have the DirectX SDK installed, which will give you the fxc.exe compiler, used to compile shader code.

我使用以下命令行编译了上面的内容:

I compiled the above with this command line:

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

在这里,您当然要用实际的文件名替换<my shader file>.

Where, of course, you replace <my shader file> with your actual file name.

(注意:我是手动完成的,但是您当然可以在项目中创建自定义构建操作来执行此操作.)

(Note: I did this manually, but you can of course create a custom build action in your project to do the same.)

然后您可以在项目中包含.ps文件,将构建操作" 设置为资源" .

You can then include the .ps file in your project, setting the "Build Action" to "Resource".

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

That done, you now need to create the ShaderEffect class that will use it. That looks like this:

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:方案将其称为"pack://application:,,,/<my shader file>.ps".同样,您当然需要将<my shader file>替换为实际的文件名.
  • 在构造函数中,您必须PixelShader属性设置为着色器对象.对于用作着色器输入的每个属性,您还必须调用UpdateShaderValue()初始化着色器(在这种情况下,只有一个).
  • Input属性是特殊的:它需要使用RegisterPixelShaderSamplerProperty()来注册依赖项属性.
  • 如果您的着色器具有其他参数,则它们将正常注册到DependencyProperty.Register().但是它们将需要一个特殊的PropertyChangedCallback值,该值可以通过调用
  • You only need one copy of the shader itself. So I initialize this into a static readonly field. Since the .ps file is included as a resource, I can refer to it using the pack: scheme, as "pack://application:,,,/<my shader file>.ps". Again, you will need to replace <my shader file> with the actual file name, of course.
  • In the constructor, you must set the PixelShader property to the shader object. You must also call UpdateShaderValue() to initialize the shader, for each property used as input to the shader (in this case, there's only the one).
  • The Input property is special: it requires the use of RegisterPixelShaderSamplerProperty() to register the dependency property.
  • If your shader had other parameters, they would be registered normally with DependencyProperty.Register(). But they would require a special PropertyChangedCallback value, obtained by calling ShaderEffect.PixelShaderConstantCallback() with the register index declared in the shader code for that parameter.

仅此而已!

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

You can use the above in XAML simply by setting a UIElement.Effect property to an instance of the InvertEffect class. For example:

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

运行该命令时,您会注意到,即使将渐变在左上角定义为黑色,然后在右下角过渡为白色,也以相反的方式显示,左上角为白色,黑色为黑色在右下角.

When you run that, you'll notice that even though the gradient is defined as black in the upper-left transitioning to white in the lower-right, it's displayed the opposite way, with white in the upper-left and black in the lower-right.

最后,如果您不希望使用此功能,则可以立即使它立即运行,而无权访问fxc.exe编译器,以下是上述版本的版本,该版本将已编译的着色器代码嵌入为Base64.它很小,因此这是编译并将着色器作为资源包括在内的一种实用替代方法.

Finally, on the off-chance you want to just get this working immediately and don't have access to the fxc.exe compiler, here's a version of the above that has the compiled shader code embedded as Base64. It's tiny, so this is a practical alternative to compiling and including the shader as a resource.

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

}

最后,我会注意到

Finally, I'll note that the link offered in Bradley's comment does have a whole bunch of these kinds of shader-implemented effects. The author of those implemented the HLSL and the ShaderEffect objects only slightly differently from the way I show here, so if you want to see other examples of effects and different ways to implement them, browsing that code would be a great place to look.

享受!

这篇关于如何使用C#转换XAML PNG图像的颜色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆