如何使用 C# 反转 XAML PNG 图像的颜色?
Posted
技术标签:
【中文标题】如何使用 C# 反转 XAML PNG 图像的颜色?【英文标题】:How to Invert Color of XAML PNG Images using C#? 【发布时间】:2017-12-18 23:45:43 【问题描述】:我正在使用 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>
【问题讨论】:
这些链接可能会有所帮助:Using ColorMatrix for Creating Negative Image 和 WPF - Modifying Image Colors on the Fly (C#) This library 带有几个预定义的 WPF 着色器效果,包括颜色反转。 【参考方案1】:感谢您提出这个问题。它给了我一个学习新东西的机会。 :)
您的目标是,一旦您知道自己在做什么,就很容易实现。 WPF 支持使用 GPU 着色器来修改图像。它们在运行时速度很快(因为它们在您的视频卡中执行)并且易于应用。并且在反转颜色的既定目标的情况下,也很容易实现。
首先,您需要着色器代码。着色器是用一种称为High Level Shader Language 或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.ps .hlsl
当然,您将 <my shader file>
替换为您的实际文件名。
(注意:我手动完成了此操作,但您当然可以在项目中创建自定义构建操作来执行相同操作。)
然后您可以在项目中包含.ps
文件,将“构建操作” 设置为“资源”。
完成后,您现在需要创建将使用它的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:
方案引用它,如"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's comment 中提供的链接确实有一大堆这类着色器实现的效果。那些实现 HLSL 和 ShaderEffect
对象的作者与我在此处展示的方式略有不同,因此如果您想查看其他效果示例和实现它们的不同方式,浏览该代码将是一个很好的地方.
享受吧!
【讨论】:
非常感谢。对于将来的参考,使用here 中建议的方法首先通过color.rgb /= color.a
计算像素颜色的alpha 印象,然后通过color.rgb = 1 - color.rgb
应用invert,然后通过color.rgb *= color.a
添加alpha,对于具有alpha 通道的像素可以获得更好的结果。 以上是关于如何使用 C# 反转 XAML PNG 图像的颜色?的主要内容,如果未能解决你的问题,请参考以下文章