1

I want to change the saturation of an Image based on a bool value.

<Image Source="/Images/256/dialog-error-4.png" />

Original

enter image description here

What I want - changed saturation

enter image description here

Is it possible to achieve this by a hidden function which I don't know or should I create duplicates of my images and replace them inside of my Controls by Trigger?


Solution

Source: http://bursjootech.blogspot.com/2008/06/grayscale-effect-pixel-shader-effect-in.html

GrayscaleEffect.cs

public class GrayscaleEffect : ShaderEffect
{
    private static readonly PixelShader _PixelShader = new PixelShader() { UriSource = new Uri(@"pack://application:,,,/MyApplicationName;component/Effects/GrayscaleEffect.ps") };

    // ##############################################################################################################################
    // Properties
    // ##############################################################################################################################

    #region Properties

    public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0);
    public Brush Input
    {
        get => (Brush)GetValue(InputProperty);
        set => SetValue(InputProperty, value);
    }

    public static readonly DependencyProperty DesaturationFactorProperty = DependencyProperty.Register("DesaturationFactor", typeof(double), typeof(GrayscaleEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0), CoerceDesaturationFactor));
    public double DesaturationFactor
    {
        get => (double)GetValue(DesaturationFactorProperty);
        set => SetValue(DesaturationFactorProperty, value);
    }

    #endregion

    // ##############################################################################################################################
    // Constructor
    // ##############################################################################################################################

    #region Constructor

    public GrayscaleEffect()
    {
        PixelShader = _PixelShader;

        UpdateShaderValue(InputProperty);
        UpdateShaderValue(DesaturationFactorProperty);
    }

    #endregion

    // ##############################################################################################################################
    // private methods
    // ##############################################################################################################################

    #region private methods

    private static object CoerceDesaturationFactor(DependencyObject d, object value)
    {
        GrayscaleEffect effect = (GrayscaleEffect)d;
        double newFactor = (double)value;

        if (newFactor < 0.0 || newFactor > 1.0)
        {
            return effect.DesaturationFactor;
        }

        return newFactor;
    }

    #endregion        
}

GrayscaleEffect.fx

In the GrayscaleEffect-project, add two new files (Add -> New Item). GrayscaleEffect.fx and GrayscaleEffect.ps as Text-file. Make sure that GrayscaleEffect.ps is set as a Resource (in Build Action) in Properties. NOTE: The GrayscaleEffect.fx must be in ANSI format. Convert it to ANSI by opening the file in Notepad and save it, select ANSI format in the save dialog.

sampler2D implicitInput : register(s0);
float factor : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 color = tex2D(implicitInput, uv);
    float gray = color.r * 0.3 + color.g * 0.59 + color.b *0.11;    

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

GrayscaleEffect.ps

compiled source file: https://files.dominic-jonas.de/stackoverflow/GrayscaleEffect.ps

Dominic Jonas
  • 3,671
  • 1
  • 25
  • 55
  • You could use the `FormatConvertedBitmap` class to convert an image to grayscale if that's what you want to do: https://docs.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-convert-an-image-to-greyscale – mm8 Jul 19 '18 at 07:45
  • Neither (no hidden function nor another Image element). Explicitly create a BitmapSource (e.g. a BitmapImage) and assign or bind that to the Source property of the Image. Then replace the BitmapSource. – Clemens Jul 19 '18 at 07:45
  • A FormatConverterBitmap with a grayscale DestinationFormat would lose background transparency. Do you want to switch between color and grayscale at runtime? – Clemens Jul 19 '18 at 07:51
  • If the background transparency is lost using `FormatConvertedBitmap`, I can't use it. So I will have a look at the following post https://stackoverflow.com/questions/2265910/convert-an-image-to-grayscale and make a custom `Image` control which supports this 'behavior' – Dominic Jonas Jul 19 '18 at 07:58
  • Don't do that. That's WinForms. And you do of course not need a custom Image element. I ask it again, do you need to switch between color and grayscale at runtime? – Clemens Jul 19 '18 at 07:59
  • Ah sorry, I overread that. Yes I want to switch it at runtime. – Dominic Jonas Jul 19 '18 at 08:02
  • And do you only need grayscale or an actual color saturation value between 0 and 1? – Clemens Jul 19 '18 at 08:05
  • 1
    Yes. Just to switch between. I found an old answer https://stackoverflow.com/questions/4708241/convertor-on-imagesource-convert-image-to-grayscale where someone wrote an `effect` http://bursjootech.blogspot.com/2008/06/grayscale-effect-pixel-shader-effect-in.html This seems to be the right direction – Dominic Jonas Jul 19 '18 at 08:08

0 Answers0