I have a solution you may like. The sample looks like this:
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!