diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml
index aeacbdc1f4ce..eabd01455425 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml
@@ -10,11 +10,15 @@
d:DesignWidth="400">
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
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 ea7c69dc3a4a..cffefa519359 100644
--- a/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs
+++ b/src/SamplesApp/UITests.Shared/Windows_UI_Composition/EffectBrushTests.xaml.cs
@@ -3,6 +3,8 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
+using ShimSkiaSharp;
+using SkiaSharp;
using Uno.Extensions;
using Uno.UI.Samples.Controls;
using Windows.Foundation;
@@ -79,6 +81,39 @@ private void EffectBrushTests_Loaded(object sender, RoutedEventArgs e)
var effectBrush6 = factory6.CreateBrush();
tintGrid.Background = new EffectTesterBrush(effectBrush6);
+
+ var effect7 = new SimpleBlendEffect() { Background = new CompositionEffectSourceParameter("sourceBrush"), Foreground = new CompositionEffectSourceParameter("secondaryBrush"), Mode = D2D1BlendEffectMode.Color };
+ var factory7 = compositor.CreateEffectFactory(effect7);
+ var effectBrush7 = factory7.CreateBrush();
+
+ blendGrid.Background = new EffectTesterBrushWithSecondaryBrush(effectBrush7, compositor.CreateColorBrush(Colors.LightBlue));
+
+ var surface = LoadedImageSurface.StartLoadFromUri(new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Logo-winui.svg/200px-Logo-winui.svg.png"));
+ surface.LoadCompleted += (s, o) =>
+ {
+ if (o.Status == LoadedImageSourceLoadStatus.Success)
+ {
+ var brush = compositor.CreateSurfaceBrush(surface);
+
+ var effect8 = new SimpleCompositeEffect() { Sources = { new CompositionEffectSourceParameter("secondaryBrush"), new CompositionEffectSourceParameter("sourceBrush") }, Mode = D2D1CompositeMode.SourceOver };
+ var factory8 = compositor.CreateEffectFactory(effect8);
+ var effectBrush8 = factory8.CreateBrush();
+
+ compositeGrid.Background = new EffectTesterBrushWithSecondaryBrush(effectBrush8, brush);
+ }
+ };
+
+ var effect9 = new SimpleColorSourceEffect() { Color = Color.FromArgb(127, 66, 135, 245) };
+ var factory9 = compositor.CreateEffectFactory(effect9);
+ var effectBrush9 = factory9.CreateBrush();
+
+ colorGrid.Background = new EffectTesterBrush(effectBrush9);
+
+ var effect10 = new SimpleOpacityEffect() { Source = new CompositionEffectSourceParameter("sourceBrush"), Opacity = 0.2f };
+ var factory10 = compositor.CreateEffectFactory(effect10);
+ var effectBrush10 = factory10.CreateBrush();
+
+ opacityGrid.Background = new EffectTesterBrush(effectBrush10);
#endif
}
@@ -105,11 +140,36 @@ protected override void OnConnected()
}
}
-#if WINDOWS_UWP // Making the sample buildable on UWP
- private interface IGraphicsEffectD2D1Interop { }
- private enum GraphicsEffectPropertyMapping { Direct, RadiansToDegrees, ColorToVector4 }
-#endif
+ private class EffectTesterBrushWithSecondaryBrush : XamlCompositionBrushBase
+ {
+ private CompositionEffectBrush _effectBrush;
+ private CompositionBrush _secondaryBrush;
+
+ public EffectTesterBrushWithSecondaryBrush(CompositionEffectBrush effectBrush, CompositionBrush secondaryBrush)
+ {
+ _effectBrush = effectBrush;
+ _secondaryBrush = secondaryBrush;
+ }
+
+ protected override void OnConnected()
+ {
+ var compositor = Window.Current.Compositor;
+ var surface = LoadedImageSurface.StartLoadFromUri(new Uri("https://avatars.githubusercontent.com/u/52228309?s=200&v=4"));
+ surface.LoadCompleted += (s, o) =>
+ {
+ if (o.Status == LoadedImageSourceLoadStatus.Success)
+ {
+ var brush = compositor.CreateSurfaceBrush(surface);
+ _effectBrush.SetSourceParameter("sourceBrush", brush);
+ _effectBrush.SetSourceParameter("secondaryBrush", _secondaryBrush);
+ CompositionBrush = _effectBrush;
+ }
+ };
+ }
+ }
+
+#if !WINDOWS_UWP
[Guid("1FEB6D69-2FE6-4AC9-8C58-1D7F93E7A6A5")]
private class SimpleBlurEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
@@ -289,7 +349,7 @@ public object GetProperty(uint index)
[Guid("36312B17-F7DD-4014-915D-FFCA768CF211")]
private class SimpleTintEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
{
- private string _name = "SimpleHueRotationEffect";
+ private string _name = "SimpleTintEffect";
private Guid _id = new Guid("36312B17-F7DD-4014-915D-FFCA768CF211");
public string Name
@@ -338,5 +398,222 @@ public object GetProperty(uint index)
public IGraphicsEffectSource GetSource(uint index) => Source;
public uint GetSourceCount() => 1;
}
+
+ [Guid("81C5B77B-13F8-4CDD-AD20-C890547AC65D")]
+ private class SimpleBlendEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
+ {
+ private string _name = "SimpleBlendEffect";
+ private Guid _id = new Guid("81C5B77B-13F8-4CDD-AD20-C890547AC65D");
+
+ public string Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ public D2D1BlendEffectMode Mode { get; set; } = D2D1BlendEffectMode.Multiply;
+
+ public IGraphicsEffectSource Background { get; set; }
+
+ public IGraphicsEffectSource Foreground { get; set; }
+
+ public Guid GetEffectId() => _id;
+
+ public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
+ {
+ switch (name)
+ {
+ case "Mode":
+ {
+ index = 0;
+ mapping = GraphicsEffectPropertyMapping.Direct;
+ break;
+ }
+ default:
+ {
+ index = 0xFF;
+ mapping = (GraphicsEffectPropertyMapping)0xFF;
+ break;
+ }
+ }
+ }
+
+ public object GetProperty(uint index)
+ {
+ switch (index)
+ {
+ case 0:
+ return Mode;
+ default:
+ return null;
+ }
+ }
+
+ public uint GetPropertyCount() => 1;
+
+ public IGraphicsEffectSource GetSource(uint index) => index is 0 ? Background : Foreground;
+
+ public uint GetSourceCount() => 2;
+ }
+
+ [Guid("48FC9F51-F6AC-48F1-8B58-3B28AC46F76D")]
+ private class SimpleCompositeEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
+ {
+ private string _name = "SimpleCompositeEffect";
+ private Guid _id = new Guid("48FC9F51-F6AC-48F1-8B58-3B28AC46F76D");
+
+ public string Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ public D2D1CompositeMode Mode { get; set; } = D2D1CompositeMode.SourceOver;
+
+ public List Sources { get; set; } = new();
+
+ public Guid GetEffectId() => _id;
+
+ public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
+ {
+ switch (name)
+ {
+ case "Mode":
+ {
+ index = 0;
+ mapping = GraphicsEffectPropertyMapping.Direct;
+ break;
+ }
+ default:
+ {
+ index = 0xFF;
+ mapping = (GraphicsEffectPropertyMapping)0xFF;
+ break;
+ }
+ }
+ }
+
+ public object GetProperty(uint index)
+ {
+ switch (index)
+ {
+ case 0:
+ return Mode;
+ default:
+ return null;
+ }
+ }
+
+ public uint GetPropertyCount() => 1;
+
+ public IGraphicsEffectSource GetSource(uint index) => index < Sources.Count ? Sources[(int)index] : null;
+
+ public uint GetSourceCount() => (uint)Sources.Count;
+ }
+
+ [Guid("61C23C20-AE69-4D8E-94CF-50078DF638F2")]
+ private class SimpleColorSourceEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
+ {
+ private string _name = "SimpleColorSourceEffect";
+ private Guid _id = new Guid("61C23C20-AE69-4D8E-94CF-50078DF638F2");
+
+ public string Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ public Color Color { get; set; } = Color.FromArgb(255, 255, 255, 255);
+
+ public Guid GetEffectId() => _id;
+
+ public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
+ {
+ switch (name)
+ {
+ case "Color":
+ {
+ index = 0;
+ mapping = GraphicsEffectPropertyMapping.ColorToVector4;
+ break;
+ }
+ default:
+ {
+ index = 0xFF;
+ mapping = (GraphicsEffectPropertyMapping)0xFF;
+ break;
+ }
+ }
+ }
+
+ public object GetProperty(uint index)
+ {
+ switch (index)
+ {
+ case 0:
+ return Color;
+ default:
+ return null;
+ }
+ }
+
+ public uint GetPropertyCount() => 1;
+ public IGraphicsEffectSource GetSource(uint index) => throw new InvalidOperationException();
+ public uint GetSourceCount() => 0;
+ }
+
+ [Guid("811D79A4-DE28-4454-8094-C64685F8BD4C")]
+ private class SimpleOpacityEffect : IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop
+ {
+ private string _name = "SimpleOpacityEffect";
+ private Guid _id = new Guid("811D79A4-DE28-4454-8094-C64685F8BD4C");
+
+ public string Name
+ {
+ get => _name;
+ set => _name = value;
+ }
+
+ public float Opacity { get; set; } = 1.0f;
+
+ public IGraphicsEffectSource Source { get; set; }
+
+ public Guid GetEffectId() => _id;
+
+ public void GetNamedPropertyMapping(string name, out uint index, out GraphicsEffectPropertyMapping mapping)
+ {
+ switch (name)
+ {
+ case "Opacity":
+ {
+ index = 0;
+ mapping = GraphicsEffectPropertyMapping.Direct;
+ break;
+ }
+ default:
+ {
+ index = 0xFF;
+ mapping = (GraphicsEffectPropertyMapping)0xFF;
+ break;
+ }
+ }
+ }
+
+ public object GetProperty(uint index)
+ {
+ switch (index)
+ {
+ case 0:
+ return Opacity;
+ 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 2421da1f0697..03e8dc5c66a8 100644
--- a/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs
+++ b/src/Uno.UI.Composition/Composition/CompositionEffectBrush.skia.cs
@@ -199,6 +199,96 @@ half4 main()
*/
}
+ return null;
+ }
+ case EffectType.BlendEffect: // TODO: Replace this with a pixel shader to get the same output as Windows
+ {
+ if (effectInterop.GetSourceCount() == 2 && effectInterop.GetPropertyCount() == 1 && effectInterop.GetSource(0) is IGraphicsEffectSource bg && effectInterop.GetSource(1) is IGraphicsEffectSource fg)
+ {
+ SKImageFilter bgFilter = GenerateEffectFilter(bg, bounds);
+ if (bgFilter is null)
+ return null;
+
+ SKImageFilter fgFilter = GenerateEffectFilter(fg, bounds);
+ if (fgFilter is null)
+ return null;
+
+ effectInterop.GetNamedPropertyMapping("Mode", out uint modeProp, out _);
+ D2D1BlendEffectMode mode = (D2D1BlendEffectMode)effectInterop.GetProperty(modeProp);
+ SKBlendMode skMode = mode.ToSkia();
+
+ if (skMode == (SKBlendMode)0xFF) // Unsupported mode
+ return null;
+
+ return SKImageFilter.CreateBlendMode(skMode, bgFilter, fgFilter, new(bounds));
+ }
+
+ return null;
+ }
+ case EffectType.CompositeEffect:
+ {
+ if (effectInterop.GetSourceCount() > 1 && effectInterop.GetPropertyCount() == 1)
+ {
+ SKImageFilter currentFilter = GenerateEffectFilter(effectInterop.GetSource(0), bounds);
+ if (currentFilter is null)
+ return null;
+
+ effectInterop.GetNamedPropertyMapping("Mode", out uint modeProp, out _);
+ D2D1CompositeMode mode = (D2D1CompositeMode)effectInterop.GetProperty(modeProp);
+ SKBlendMode skMode = mode.ToSkia();
+
+ if (skMode == (SKBlendMode)0xFF) // Unsupported mode
+ return null;
+
+ for (uint idx = 1; idx < effectInterop.GetSourceCount(); idx++)
+ {
+ SKImageFilter nextFilter = GenerateEffectFilter(effectInterop.GetSource(idx), bounds);
+
+ if (nextFilter is not null)
+ currentFilter = SKImageFilter.CreateBlendMode(skMode, currentFilter, nextFilter, new(bounds));
+ }
+
+ return currentFilter;
+ }
+
+ return null;
+ }
+ case EffectType.ColorSourceEffect:
+ {
+ if (effectInterop.GetPropertyCount() >= 1 /* only the Color property is required */)
+ {
+ // Note: ColorHdr isn't supported by Composition (as of 10.0.25941.1000)
+ effectInterop.GetNamedPropertyMapping("Color", out uint colorProp, out _);
+ Color color = (Color)effectInterop.GetProperty(colorProp);
+
+ return SKImageFilter.CreatePaint(new SKPaint() { Color = color.ToSKColor() }, new(bounds));
+ }
+
+ return null;
+ }
+ case EffectType.OpacityEffect:
+ {
+ if (effectInterop.GetSourceCount() == 1 && effectInterop.GetPropertyCount() == 1 && effectInterop.GetSource(0) is IGraphicsEffectSource source)
+ {
+ SKImageFilter sourceFilter = GenerateEffectFilter(source, bounds);
+ if (sourceFilter is null)
+ return null;
+
+ effectInterop.GetNamedPropertyMapping("Opacity", out uint opacityProp, out _);
+ float opacity = (float)effectInterop.GetProperty(opacityProp);
+
+ return SKImageFilter.CreateColorFilter(
+ SKColorFilter.CreateColorMatrix(
+ new float[] // Opacity Matrix
+ {
+ 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, opacity, 0
+ }),
+ sourceFilter, new(bounds));
+ }
+
return null;
}
case EffectType.Unsupported: