V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yanjinhua
V2EX  ›  C#

如何对整个 WPF 应用程序进行灰度

  •  
  •   yanjinhua · 2022-12-28 14:18:22 +08:00 · 945 次点击
    这是一个创建于 483 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如何对整个 WPF 应用程序进行灰度

    控件名:GrayscaleEffect

    作 者:WPFDevelopersOrg - 驚鏵

    原文链接https://github.com/WPFDevelopersOrg/WPFDevelopers 简易源码

    • 框架使用.NET40

    • Visual Studio 2019;

    • 如果要实现灰度第一反是使用主题色更改,但是使用主题色需要重新配色比较慢,需 Effect 类派生以实现自定义位图效果,将使用ShaderEffect进行更改灰度,从 ShaderEffect 类派生,以基于单个像素着色器实现自定义效果。

    • 必须安装 .NET Framework 3.5 sp1 或更高版本才能正常工作。

    • 需要安装DirectX SDK才能编译像素着色器效果。
      • PixelShader从预编译的高级着色语言 (HLSL) 字节代码加载 。
      • 定义表示效果参数和基于表面输入的 Brush 依赖属性。 使用其中 RegisterPixelShaderSamplerProperty 一个重载将这些输入与 HLSL 字节码中引用的寄存器号相关联。
      • DirectX SDK
      • 下载完成DirectX SDK安装完成。

    • 到安装目录下\Utilities\bin\x86执行以下命令,就会输出.ps文件。
    • Nuget 最新 Install-Package WPFDevelopers 1.0.9.5-preview
    • Nuget 最新 Install-Package WPFDevelopers..Minimal 1.0.0.1-preview
    • 推荐使用Shazzam Shader Editor进行编辑。

    1 ) GrayscaleEffect.hlsl 代码如下:

    sampler2D implicitInput : register(s0);
    float factor : register(c0);
    
    /// <summary>The brightness offset.</summary>
    /// <minValue>-1</minValue>
    /// <maxValue>1</maxValue>
    /// <defaultValue>0</defaultValue>
    float brightness : register(c1);
    
    float4 main(float2 uv : TEXCOORD) : COLOR
    {
        float4 pixelColor = tex2D(implicitInput, uv);
      pixelColor.rgb /= pixelColor.a;
        
      // Apply brightness.
      pixelColor.rgb += brightness;
        
      // Return final pixel color.
      pixelColor.rgb *= pixelColor.a;
    
        float4 color = pixelColor;
    
        float pr = .299;
        float pg = .587;
        float pb = .114;
        
        float gray = sqrt(color.r * color.r * pr + color.g * color.g * pg + color.b * color.b * pb);
    
      float4 result;    
      result.r = (color.r - gray) * factor + gray;
      result.g = (color.g - gray) * factor + gray;
      result.b = (color.b - gray) * factor + gray;
      result.a = color.a;
        
      return result;
    }
    

    2 )执行命令 代码如下:

      fxc /T ps_2_0 /Fo E:\GrayscaleEffect\GrayscaleEffect.ps E:\GrayscaleEffect\GrayscaleEffect.hlsl
    

    3 )得到以下文件

    4 )新建GrayscaleEffect.cs 代码如下:

    using System;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Effects;
    
    namespace WPFDevelopers
    {
        public class GrayscaleEffect : ShaderEffect
        {
            /// <summary>
            /// Identifies the Input property.
            /// </summary>
            public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0);
    
            /// <summary>
            /// Identifies the Factor property.
            /// </summary>
            public static readonly DependencyProperty FactorProperty = DependencyProperty.Register("Factor", typeof(double), typeof(GrayscaleEffect), new UIPropertyMetadata(0D, PixelShaderConstantCallback(0)));
    
            /// <summary>
            /// Identifies the Brightness property.
            /// </summary>
            public static readonly DependencyProperty BrightnessProperty = DependencyProperty.Register("Brightness", typeof(double), typeof(GrayscaleEffect), new UIPropertyMetadata(0D, PixelShaderConstantCallback(1)));
    
            /// <summary>
            /// Creates a new instance of the <see cref="GrayscaleEffect"/> class.
            /// </summary>
            public GrayscaleEffect()
            {
                var pixelShader = new PixelShader();
                pixelShader.UriSource = new Uri("WPFDevelopers;component/Effects/GrayscaleEffect.ps", UriKind.Relative);
    
                PixelShader = pixelShader;
    
                UpdateShaderValue(InputProperty);
                UpdateShaderValue(FactorProperty);
                UpdateShaderValue(BrightnessProperty);
            }
    
            /// <summary>
            /// Gets or sets the <see cref="Brush"/> used as input for the shader.
            /// </summary>
            public Brush Input
            {
                get => ((Brush)(GetValue(InputProperty)));
                set => SetValue(InputProperty, value);
            }
    
            /// <summary>
            /// Gets or sets the factor used in the shader.
            /// </summary>
            public double Factor
            {
                get => ((double)(GetValue(FactorProperty)));
                set => SetValue(FactorProperty, value);
            }
    
            /// <summary>
            /// Gets or sets the brightness of the effect.
            /// </summary>
            public double Brightness
            {
                get => ((double)(GetValue(BrightnessProperty)));
                set => SetValue(BrightnessProperty, value);
            }
        }
    }
    
    

    5 )使用Window.Xaml 代码如下,默认原色:

    <wpfdev:Window x:Class="WPFDevelopers.Samples.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:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers">
        <wpfdev:Window.Effect>
            <wpfdev:GrayscaleEffect x:Name="grayscaleEffect" Factor="1"/>
        </wpfdev:Window.Effect>
      <TextBlock Text="开启程序灰度" FontSize="20" Margin="0,20,0,0"/>
      <ToggleButton Margin="10" Name="tbGrayscale" 
                    Checked="tbGrayscale_Checked"
                    Unchecked="tbGrayscale_Unchecked"
                    HorizontalAlignment="Left"/>
    

    6 )使用Window.Xaml.cs 灰度代码如下:

    private void tbGrayscale_Checked(object sender, RoutedEventArgs e)
            {
                Create(0);
            }
            void Create(double to)
            {
                var sineEase = new SineEase() { EasingMode = EasingMode.EaseOut };
                var doubleAnimation = new DoubleAnimation
                {
                    To = to,
                    Duration = TimeSpan.FromMilliseconds(1000),
                    EasingFunction = sineEase
                };
                grayscaleEffect.BeginAnimation(GrayscaleEffect.FactorProperty, doubleAnimation);
            }
            private void tbGrayscale_Unchecked(object sender, RoutedEventArgs e)
            {
                Create(1);
            }
    

    2 条回复    2022-12-29 15:36:09 +08:00
    Soar360
        1
    Soar360  
       2022-12-28 15:04:28 +08:00
    支持支持
    yanjinhua
        2
    yanjinhua  
    OP
       2022-12-29 15:36:09 +08:00
    @Soar360 头像看起来是熟人哟
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1014 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 20:13 · PVG 04:13 · LAX 13:13 · JFK 16:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.