Does UWP Composition Api support color replacement?
Asked Answered
B

1

11

I've been trying to look for an examples which related to Color Replacement, here's an example using the Photoshop which can take, for example, a Blue shade and replace it with a Red shade:

BEFORE: enter image description here

AFTER: enter image description here

Is this possible using the Composition Effects in the latest version of Composition Api?

I've seen examples related to Hue Rotations and Temperature and Tint:

https://xamlbrewer.wordpress.com/2016/04/08/uwp-composition-effects-hue-rotation/

https://xamlbrewer.wordpress.com/2016/04/19/uwp-composition-effects-temperature-and-tint/

But I'm wondering if the api is capable of using Effects to switch a color range in an image ??

Bakken answered 1/12, 2017 at 16:21 Comment(1)
Here's the ColorMatrixEffect mentioned by Johhny for anyone who wants to further look into such a feature: microsoft.github.io/Win2D/html/… I also did stumble on a blog which uses Effects to alter the Tint/Temperature of an image. It not's exactly what I wanted but it's close and I can use it. blog.robmikh.com/uwp/composition/2016/04/21/…Bakken
I
4

I have a solution you may like. The sample looks like this:

enter image description here

To achieve this I used 3 effects in a chain: PixelShaderEffect, GaussianBlurEffect and BlendEffect from Win2d API.

XAML has CanvasAnimatedControl to draw the result plus some helpers like source (the color we want to replace) and replace color pickers and threshold slider.

<Grid>
    <xaml:CanvasAnimatedControl x:Name="AnimatedControl"
                            CreateResources="AnimatedControl_OnCreateResources"
                            Draw="AnimatedControl_OnDraw"/>

    <StackPanel HorizontalAlignment="Left" VerticalAlignment="Bottom">
        <TextBlock Text="Source Color" FontSize="32" Foreground="White" TextAlignment="Center"/>

        <ColorSpectrum Width="256" Height="256" ColorChanged="ColorSpectrum_OnColorChanged"/>
    </StackPanel>

    <StackPanel HorizontalAlignment="Right" VerticalAlignment="Bottom">
        <TextBlock Text="Replace Color" FontSize="32" Foreground="White" TextAlignment="Center"/>

        <ColorSpectrum Width="256" Height="256" ColorChanged="ColorSpectrum_OnColorChanged1"/>
    </StackPanel>

    <Slider Width="512" ValueChanged="RangeBase_OnValueChanged" VerticalAlignment="Center"/>
</Grid>

Code behind:

    private PixelShaderEffect _textureShader;
    private GaussianBlurEffect _blur;
    private BlendEffect _blend;

    private void AnimatedControl_OnCreateResources(CanvasAnimatedControl sender, CanvasCreateResourcesEventArgs args)
    {
        args.TrackAsyncAction(CreateResourcesAsync(sender).AsAsyncAction());
    }

    private async Task CreateResourcesAsync(CanvasAnimatedControl sender)
    {
        var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Shaders/TextureTest.bin"));
        var buffer = await FileIO.ReadBufferAsync(file);

        var sourceImage = await CanvasBitmap.LoadAsync(sender, new Uri("ms-appx:///Assets/image.jpg"));

        _textureShader = new PixelShaderEffect(buffer.ToArray())
        {
            Source1 = sourceImage
        };

        _blur = new GaussianBlurEffect
        {
            BlurAmount = 4,
            Source = _textureShader
        };

        _blend = new BlendEffect
        {
            Foreground = _blur,
            Background = sourceImage,
            Mode = BlendEffectMode.Color
        };
    }

    private void AnimatedControl_OnDraw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args)
    {
        args.DrawingSession.DrawImage(_blend);
    }

    private void RangeBase_OnValueChanged(object sender, RangeBaseValueChangedEventArgs e)
    {
        _textureShader.Properties["threshold"] = (float)e.NewValue / 100;
    }

    private void ColorSpectrum_OnColorChanged(ColorSpectrum sender, ColorChangedEventArgs args)
    {
        _textureShader.Properties["sourceColor"] = new Vector3((float)args.NewColor.R / 255, (float)args.NewColor.G / 255, (float)args.NewColor.B / 255);
    }

    private void ColorSpectrum_OnColorChanged1(ColorSpectrum sender, ColorChangedEventArgs args)
    {
        _textureShader.Properties["replaceColor"] = new Vector3((float)args.NewColor.R / 255, (float)args.NewColor.G / 255, (float)args.NewColor.B / 255);
    }

And the most exciting thing is a pixel shader:

#define D2D_INPUT_COUNT 1
#define D2D_INPUT0_SIMPLE

#include "d2d1effecthelpers.hlsli"

float3 sourceColor;
float3 replaceColor;
float threshold;

D2D_PS_ENTRY(main)
{
    float3 color = D2DGetInput(0).rgb;

    if (abs(color.r - sourceColor.r) < threshold && abs(color.g - sourceColor.g) < threshold && abs(color.b - sourceColor.b) < threshold) 
    {
        float3 newColor = color - sourceColor + replaceColor;
        return float4(newColor.r, newColor.g, newColor.b, 1);
    }
    else 
    {
        return float4(0, 0, 0, 0);
    }
}

To compile it you should take a tour here: https://github.com/Microsoft/Win2D-Samples/tree/master/ExampleGallery/Shared/Shaders

There's cmd file that compiles your hlsl shaders. If you need some help - comment it. Have fun!

Incus answered 31/1, 2018 at 17:50 Comment(8)
Wow!! That is amazing!!! Thanks for the detailed explanation! I would've never figured this out.Bakken
Out of curiosity, why is a blur effect involved if there is no blurring in the output.Bakken
Without blur the output would be too pixelated. You may set BlurAmount = 0 to observe the difference.Incus
Chopper, quick question, since I've never worked with Win2D. Where is the link between the code and the HSLS shader? I don't see it referenced in the code-behind.Bakken
When you compile hlsl shader you receive a binary file in my case it's TextureTest.bin (my shader is called TextureTest.hlsl). Don't forget to include this file in your project and select build action as Content in it's properties.Incus
Hi again Jet, the shader is working really well, and I managed to get the image to stretch correctly. If I want to take this a step further, for example, allow 2 or 3 colors to be replaced simultaneously, I'm assuming I would have to set a Source2 and Source3 for the PixelShader and modify the hlsl file to have more inputs? Also, from looking at the hlsl code, I would also add sourceColor2, replacecolor2, etc...Bakken
Yes, Win2d PixelShaderEffect supports up to 8 texture inputs and a lot of propeties. You may modify shader as you wish just follow the same algorithmIncus
Followed the pattern, got stuck on the "return" value. I made an issue but no one figured it out. I figured you can gain more points if you know the answer :) #48599391Bakken

© 2022 - 2024 — McMap. All rights reserved.