Browse Source

Enable VisualBrush to be used as Visual's OpacityMask (#17072)

* Enable VisualBrush to be used as Visual's OpacityMask

* Add missing test file

---------

Co-authored-by: Benedikt Stebner <Gillibald@users.noreply.github.com>
pull/17011/head
Nikita Tsukanov 1 year ago
committed by GitHub
parent
commit
2299d96f98
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 21
      src/Avalonia.Base/Rendering/Composition/Visual.cs
  2. 3
      src/Avalonia.Base/Visual.Composition.cs
  3. 2
      src/Avalonia.Base/composition-schema.xml
  4. 4
      src/tools/DevGenerators/CompositionGenerator/Config.cs
  5. 5
      src/tools/DevGenerators/CompositionGenerator/Generator.cs
  6. 52
      tests/Avalonia.RenderTests/Media/VisualBrushTests.cs
  7. BIN
      tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png
  8. BIN
      tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png

21
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<IBrush> oldCompositorBrush)
{
oldCompositorBrush.ReleaseOnCompositor(Compositor);
_opacityMask = null;
OpacityMaskBrushTransportField = null;
}
if (value is ICompositionRenderResource<IBrush> newCompositorBrush)
{
newCompositorBrush.AddRefOnCompositor(Compositor);
OpacityMaskBrushTransportField = newCompositorBrush.GetForCompositor(Compositor);
_opacityMask = value;
}
else
OpacityMaskBrushTransportField = (_opacityMask = value)?.ToImmutable();
}
}

3
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();

2
src/Avalonia.Base/composition-schema.xml

@ -31,7 +31,7 @@
<Property Name="TransformMatrix" Type="Avalonia.Matrix" DefaultValue="Avalonia.Matrix.Identity" Animated="true" Internal="true"/>
<Property Name="AdornedVisual" Type="CompositionVisual?" Internal="true" />
<Property Name="AdornerIsClipped" Type="bool" Internal="true" />
<Property Name="OpacityMaskBrush" Type="Avalonia.Media.IImmutableBrush?" Internal="true" />
<Property Name="OpacityMaskBrush" ClientName="OpacityMaskBrushTransportField" Type="Avalonia.Media.IBrush?" Private="true" />
<Property Name="Effect" Type="Avalonia.Media.IImmutableEffect?" Internal="true" />
<Property Name="RenderOptions" Type="Avalonia.Media.RenderOptions" />
</Object>

4
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

5
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)))),

52
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();
}
}
}

BIN
tests/TestFiles/Direct2D1/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
tests/TestFiles/Skia/Media/VisualBrush/VisualBrush_Should_Be_Usable_As_Opacity_Mask.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Loading…
Cancel
Save