diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
index 48bba84da7..5659bda1d9 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
@@ -208,7 +208,16 @@ namespace Avalonia.Rendering.SceneGraph
///
public void PopOpacity()
{
- // TODO: Implement
+ var next = NextDrawAs();
+
+ if (next == null || !next.Equals(null))
+ {
+ Add(new OpacityNode());
+ }
+ else
+ {
+ ++_drawOperationindex;
+ }
}
///
@@ -232,7 +241,16 @@ namespace Avalonia.Rendering.SceneGraph
///
public void PushOpacity(double opacity)
{
- // TODO: Implement
+ var next = NextDrawAs();
+
+ if (next == null || !next.Equals(opacity))
+ {
+ Add(new OpacityNode(opacity));
+ }
+ else
+ {
+ ++_drawOperationindex;
+ }
}
///
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs
new file mode 100644
index 0000000000..097bf15828
--- /dev/null
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs
@@ -0,0 +1,64 @@
+using System;
+using Avalonia.Platform;
+
+namespace Avalonia.Rendering.SceneGraph
+{
+ ///
+ /// A node in the scene graph which represents an opacity push or pop.
+ ///
+ internal class OpacityNode : IDrawOperation
+ {
+ ///
+ /// Initializes a new instance of the class that represents an
+ /// opacity push.
+ ///
+ /// The opacity to push.
+ public OpacityNode(double opacity)
+ {
+ Opacity = opacity;
+ }
+
+ ///
+ /// Initializes a new instance of the class that represents an
+ /// opacity pop.
+ ///
+ public OpacityNode()
+ {
+ }
+
+ ///
+ public Rect Bounds => Rect.Empty;
+
+ ///
+ /// Gets the opacity to be pushed or null if the operation represents a pop.
+ ///
+ public double? Opacity { get; }
+
+ ///
+ public bool HitTest(Point p) => false;
+
+ ///
+ /// Determines if this draw operation equals another.
+ ///
+ /// The opacity of the other draw operation.
+ /// True if the draw operations are the same, otherwise false.
+ ///
+ /// The properties of the other draw operation are passed in as arguments to prevent
+ /// allocation of a not-yet-constructed draw operation object.
+ ///
+ public bool Equals(double? opacity) => Opacity == opacity;
+
+ ///
+ public void Render(IDrawingContextImpl context)
+ {
+ if (Opacity.HasValue)
+ {
+ context.PushOpacity(Opacity.Value);
+ }
+ else
+ {
+ context.PopOpacity();
+ }
+ }
+ }
+}
diff --git a/tests/TestFiles/Cairo/Controls/CustomRender/Opacity.expected.png b/tests/TestFiles/Cairo/Controls/CustomRender/Opacity.expected.png
new file mode 100644
index 0000000000..159f428344
Binary files /dev/null and b/tests/TestFiles/Cairo/Controls/CustomRender/Opacity.expected.png differ
diff --git a/tests/TestFiles/Skia/Controls/CustomRender/Opacity.expected.png b/tests/TestFiles/Skia/Controls/CustomRender/Opacity.expected.png
new file mode 100644
index 0000000000..399d502a56
Binary files /dev/null and b/tests/TestFiles/Skia/Controls/CustomRender/Opacity.expected.png differ