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;