我正在使用Visual Studio,C#,XAML,WPF。
在我的程序中,我有带有白色png图标的XAML按钮。
我想拥有它,所以你可以通过从ComboBox中选择主题来切换到带有黑色图标的主题。
有没有一种方法可以使用XAML和C#来反转白色图标的颜色,而不是创建一组新的黑色png图像?
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.view.ViewPager.setCurrentItem(int)' on a null object reference
答案 0 :(得分:9)
感谢您提出这个问题。它给了我一个学习新东西的机会。 :)
你的目标是,一旦你知道自己在做什么,就很容易实现。 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<my shader file>.ps <my shader file>.hlsl
当然,您可以使用实际文件名替换<my shader file>
。
(注意:我手动执行此操作,但您当然可以在项目中创建自定义构建操作以执行相同操作。)
然后,您可以在项目中包含.ps
文件,将&#34;构建操作&#34; 设置为&#34;资源&#34;
完成后,您现在需要创建将使用它的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
对象的作者与我在这里展示的方式略有不同,所以如果你想看到其他效果的例子和实现它们的不同方法,浏览那些代码将是一个好地方看。
享受!