diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml index 1fd8a0851760..43d108eeab4e 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml @@ -30,5 +30,6 @@ + diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs index 9e7a4d5b74a6..8c53c75e66f8 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs @@ -184,6 +184,21 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e) var effectBrush20 = factory20.CreateBrush(); tempTintGrid.Background = new EffectTesterBrush(effectBrush20); + + var swapRedAndBlue = new SimpleMatrix5x4 + { + M11 = 0, M12 = 0, M13 = 1, M14 = 0, + M21 = 0, M22 = 1, M23 = 0, M24 = 0, + M31 = 1, M32 = 0, M33 = 0, M34 = 0, + M41 = 0, M42 = 0, M43 = 0, M44 = 1, + M51 = 0, M52 = 0, M53 = 0, M54 = 0 + }; + + var effect21 = new SimpleColorMatrixEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), ColorMatrix = swapRedAndBlue }; + var factory21 = compositor.CreateEffectFactory(effect21); + var effectBrush21 = factory21.CreateBrush(); + + matrixGrid.Background = new EffectTesterBrush(effectBrush21); #endif } @@ -1497,6 +1512,107 @@ public object GetProperty(uint index) public IGraphicsEffectSource GetSource(uint index) => Source; public uint GetSourceCount() => 1; } + + private struct SimpleMatrix5x4 + { + public float M11; + public float M12; + public float M13; + public float M14; + public float M21; + public float M22; + public float M23; + public float M24; + public float M31; + public float M32; + public float M33; + public float M34; + public float M41; + public float M42; + public float M43; + public float M44; + public float M51; + public float M52; + public float M53; + public float M54; + + public static SimpleMatrix5x4 Identity + { + get => new SimpleMatrix5x4() + { + M11 = 1, M12 = 0, M13 = 0, M14 = 0, + M21 = 0, M22 = 1, M23 = 0, M24 = 0, + M31 = 0, M32 = 0, M33 = 1, M34 = 0, + M41 = 0, M42 = 0, M43 = 0, M44 = 1, + M51 = 0, M52 = 0, M53 = 0, M54 = 0 + }; + } + + public float[] ToArray() + { + return new float[20] + { + M11, M12, M13, M14, + M21, M22, M23, M24, + M31, M32, M33, M34, + M41, M42, M43, M44, + M51, M52, M53, M54 + }; + } + } + + [Guid("921F03D6-641C-47DF-852D-B4BB6153AE11")] + private class SimpleColorMatrixEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop + { + private string _name = "SimpleColorMatrixEffect"; + private Guid _id = new Guid("921F03D6-641C-47DF-852D-B4BB6153AE11"); + + public string Name + { + get => _name; + set => _name = value; + } + + public SimpleMatrix5x4 ColorMatrix { get; set; } = SimpleMatrix5x4.Identity; + + public IGraphicsEffectSource Source { get; set; } + + public Guid GetEffectId() => _id; + + public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping) + { + switch (name) + { + case "ColorMatrix": + { + index = 0; + mapping = GraphicsEffectPropertyMapping.Direct; + break; + } + default: + { + index = 0xFF; + mapping = (GraphicsEffectPropertyMapping)0xFF; + break; + } + } + } + + public object GetProperty(uint index) + { + switch (index) + { + case 0: + return ColorMatrix.ToArray(); + default: + return null; + } + } + + public uint GetPropertyCount() => 1; + public IGraphicsEffectSource GetSource(uint index) => Source; + public uint GetSourceCount() => 1; + } #endif } } diff --git a/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs b/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs index 9cd2d2a7de58..a59439232470 100644 --- a/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs @@ -12,7 +12,7 @@ public partial class CompositionEffectBrush : CompositionBrush private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds) { // TODO: https://user-images.githubusercontent.com/34550324/264485558-d7ee5062-b0e0-4f6e-a8c7-0620ec561d3d.png - // TODO: Cache pixel shaders (see dwmcore.dll!CCompiledEffectCache) + // TODO: Cache pixel shaders (see dwmcore.dll!CCompiledEffectCache), needed in order to implement animations and online rendering switch (effect) { @@ -45,7 +45,6 @@ private SKImageFilter GenerateEffectFilter(object effect, SKRect bounds) effectInterop.GetNamedPropertyMapping("Optimization", out uint optProp, out _); effectInterop.GetNamedPropertyMapping("BorderMode", out uint borderProp, out _); - // TODO: Implement support for other GraphicsEffectPropertyMapping values than Direct float sigma = (float)effectInterop.GetProperty(sigmaProp); _ = (uint)effectInterop.GetProperty(optProp); // TODO _ = (uint)effectInterop.GetProperty(borderProp); // TODO @@ -460,7 +459,7 @@ half4 main() return null; } - case EffectType.ExposureEffect: // TODO: We can probably replace the pixel shader with a color matrix filter instead? + case EffectType.ExposureEffect: { if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() == 1 && effectInterop.GetSource(0) is IGraphicsEffectSource source) { @@ -473,31 +472,16 @@ half4 main() float exposure = (float)effectInterop.GetProperty(exposureProp); float multiplier = MathF.Pow(2.0f, exposure); - string shader = $@" - uniform shader input; - uniform half multiplier; - - half4 main() - {{ - half4 inputColor = sample(input); - return half4(inputColor.rgb * multiplier, inputColor.a); - }} - "; - - SKRuntimeEffect runtimeEffect = SKRuntimeEffect.Create(shader, out string errors); - if (errors is not null) - return null; - - SKRuntimeEffectUniforms uniforms = new(runtimeEffect) - { - { "multiplier", multiplier } - }; - SKRuntimeEffectChildren children = new(runtimeEffect) - { - { "input", null } - }; - - return SKImageFilter.CreateColorFilter(runtimeEffect.ToColorFilter(uniforms, children), sourceFilter, new(bounds)); + return SKImageFilter.CreateColorFilter( + SKColorFilter.CreateColorMatrix( + new float[] // Exposure Matrix + { + multiplier, 0, 0, 0, 0, + 0, multiplier, 0, 0, 0, + 0, 0, multiplier, 0, 0, + 0, 0, 0, 1, 0, + }), + sourceFilter, new(bounds)); // Reference (wuceffects.dll): /* @@ -1077,7 +1061,7 @@ half4 main() 0,0,0 }; - return SKImageFilter.CreateMatrixConvolution(new SKSizeI(3, 3), identityKernel, 1f, 0f, new(1, 1), mode, true, sourceFilter, new(bounds)); + return SKImageFilter.CreateMatrixConvolution(new SKSizeI(3, 3), identityKernel, 1f, 0f, new(0, 0), mode, true, sourceFilter, new(bounds)); } return null; @@ -1124,34 +1108,41 @@ half4 main() var gains = TempAndTintUtils.NormalizedTempTintToGains(temp, tint); - string shader = $@" - uniform shader input; - - uniform half redGain; - uniform half blueGain; - - half4 main() - {{ - half4 inputColor = sample(input); - return half4(inputColor.r * redGain, inputColor.g, inputColor.b * blueGain, inputColor.a); - }} - "; + return SKImageFilter.CreateColorFilter( + SKColorFilter.CreateColorMatrix( + new float[] // TemperatureAndTint Matrix + { + gains.RedGain, 0, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 0, gains.BlueGain, 0, 0, + 0, 0, 0, 1, 0, + }), + sourceFilter, new(bounds)); + } - SKRuntimeEffect runtimeEffect = SKRuntimeEffect.Create(shader, out string errors); - if (errors is not null) + return null; + } + case EffectType.ColorMatrixEffect: // TODO: support "AlphaMode" and "ClampOutput" properties + { + if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() >= 1 && effectInterop.GetSource(0) is IGraphicsEffectSource source) + { + SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds); + if (sourceFilter is null) return null; - SKRuntimeEffectUniforms uniforms = new(runtimeEffect) - { - { "redGain", gains.RedGain }, - { "blueGain", gains.BlueGain } - }; - SKRuntimeEffectChildren children = new(runtimeEffect) - { - { "input", null } - }; + effectInterop.GetNamedPropertyMapping("ColorMatrix", out uint matrixProp, out _); + float[] matrix = (float[])effectInterop.GetProperty(matrixProp); - return SKImageFilter.CreateColorFilter(runtimeEffect.ToColorFilter(uniforms, children), sourceFilter, new(bounds)); + return SKImageFilter.CreateColorFilter( + SKColorFilter.CreateColorMatrix( + new float[] + { + matrix[0], matrix[1], matrix[2], matrix[3], matrix[16], + matrix[4], matrix[5], matrix[6], matrix[7], matrix[17], + matrix[8], matrix[9], matrix[10], matrix[11], matrix[18], + matrix[12], matrix[13], matrix[14], matrix[15], matrix[19], + }), + sourceFilter, new(bounds)); } return null;