diff --git a/src/Avalonia.Base/Rendering/Composition/Visual.cs b/src/Avalonia.Base/Rendering/Composition/Visual.cs index 4f6442bd77..35be380425 100644 --- a/src/Avalonia.Base/Rendering/Composition/Visual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Visual.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using Avalonia.Media; +using Avalonia.Rendering.Composition.Drawing; using Avalonia.VisualTree; namespace Avalonia.Rendering.Composition @@ -25,9 +26,25 @@ namespace Avalonia.Rendering.Composition get => _opacityMask; set { - if (_opacityMask == value) + if (ReferenceEquals(_opacityMask, value)) return; - OpacityMaskBrush = (_opacityMask = value)?.ToImmutable(); + + // Release the previous compositor-resource based brush + if (_opacityMask is ICompositionRenderResource oldCompositorBrush) + { + oldCompositorBrush.ReleaseOnCompositor(Compositor); + _opacityMask = null; + OpacityMaskBrushTransportField = null; + } + + if (value is ICompositionRenderResource newCompositorBrush) + { + newCompositorBrush.AddRefOnCompositor(Compositor); + OpacityMaskBrushTransportField = newCompositorBrush.GetForCompositor(Compositor); + _opacityMask = value; + } + else + OpacityMaskBrushTransportField = (_opacityMask = value)?.ToImmutable(); } } diff --git a/src/Avalonia.Base/Visual.Composition.cs b/src/Avalonia.Base/Visual.Composition.cs index d6a4e3e9be..f521f2a3f2 100644 --- a/src/Avalonia.Base/Visual.Composition.cs +++ b/src/Avalonia.Base/Visual.Composition.cs @@ -35,6 +35,7 @@ public partial class Visual CompositionVisual.Children.Remove(ChildCompositionVisual); CompositionVisual.DrawList = null; + CompositionVisual.OpacityMask = null; CompositionVisual = null; } } @@ -141,7 +142,7 @@ public partial class Visual comp.Clip = Clip?.PlatformImpl; if (!Equals(comp.OpacityMask, OpacityMask)) - comp.OpacityMask = OpacityMask?.ToImmutable(); + comp.OpacityMask = OpacityMask; if (!comp.Effect.EffectEquals(Effect)) comp.Effect = Effect?.ToImmutable(); diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index d4a1278fe7..6c48c71b51 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -31,7 +31,7 @@ - + diff --git a/src/tools/DevGenerators/CompositionGenerator/Config.cs b/src/tools/DevGenerators/CompositionGenerator/Config.cs index 72c7486dd9..d18947f0d2 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Config.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Config.cs @@ -107,6 +107,8 @@ namespace Avalonia.SourceGenerator.CompositionGenerator [XmlAttribute] public string Name { get; set; } [XmlAttribute] + public string ClientName { get; set; } + [XmlAttribute] public string Type { get; set; } [XmlAttribute] public string DefaultValue { get; set; } @@ -116,6 +118,8 @@ namespace Avalonia.SourceGenerator.CompositionGenerator public bool InternalSet { get; set; } [XmlAttribute] public bool Internal { get; set; } + [XmlAttribute] + public bool Private { get; set; } } public class GAnimationType diff --git a/src/tools/DevGenerators/CompositionGenerator/Generator.cs b/src/tools/DevGenerators/CompositionGenerator/Generator.cs index 2075295ca2..df2ea423de 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Generator.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Generator.cs @@ -310,8 +310,9 @@ namespace Avalonia.SourceGenerator.CompositionGenerator var fieldName = PropertyBackingFieldName(prop); return client .AddMembers(DeclareField(prop.Type, fieldName)) - .AddMembers(PropertyDeclaration(propType, prop.Name) - .AddModifiers(prop.Internal ? SyntaxKind.InternalKeyword : SyntaxKind.PublicKeyword) + .AddMembers(PropertyDeclaration(propType, prop.ClientName ?? prop.Name) + .AddModifiers( + prop.Private ? SyntaxKind.PrivateKeyword : prop.Internal ? SyntaxKind.InternalKeyword : SyntaxKind.PublicKeyword) .AddAccessorListAccessors( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, Block(ReturnStatement(IdentifierName(fieldName)))), diff --git a/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs b/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs index 3ed32cbaf0..bf4fc57081 100644 --- a/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs +++ b/tests/Avalonia.RenderTests/Media/VisualBrushTests.cs @@ -710,5 +710,57 @@ namespace Avalonia.Direct2D1.RenderTests.Media await RenderToFile(new RelativePointTestPrimitivesHelper(brush), testName); CompareImages(testName); } + + + [Fact] + public async Task VisualBrush_Should_Be_Usable_As_Opacity_Mask() + { + var target = new Border() + { + Padding = new Thickness(8), + Width = 920, + Height = 920, + Background = Brushes.Magenta, + OpacityMask = new VisualBrush + { + Stretch = Stretch.Fill, + TileMode = TileMode.None, + + Visual = new Border() + { + Width = 200, + Height = 200, + Padding = new Thickness(20), + Child = new Grid() + { + + ColumnDefinitions = ColumnDefinitions.Parse("*,*,*"), + RowDefinitions = RowDefinitions.Parse("*,*,*"), + Children = + { + new Border() + { + Background = Brushes.Aqua, + }, + new Border() + { + [Grid.ColumnProperty] = 1, + [Grid.RowProperty] = 2, + Background = Brushes.Aqua, + }, + new Border() + { + [Grid.ColumnProperty] = 3, + Background = Brushes.Aqua, + }, + } + }, + } + } + }; + + await RenderToFile(target); + CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png new file mode 100644 index 0000000000..4413901c56 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png differ diff --git a/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png new file mode 100644 index 0000000000..9a74a81e04 Binary files /dev/null and b/tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png differ