From 101cdc52576ceedf679b9d5fd584006c1b0bf4b3 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 17 May 2023 08:49:06 +0200 Subject: [PATCH] Fix PushOpacity when ClipToBounds is false --- src/Avalonia.Base/Media/DrawingContext.cs | 7 +++--- src/Avalonia.Base/Media/DrawingGroup.cs | 22 ++++++++++++------- .../Media/PlatformDrawingContext.cs | 4 ++-- .../Platform/IDrawingContextImpl.cs | 2 +- .../Drawing/Nodes/RenderDataNodes.cs | 5 ++--- .../Drawing/RenderDataDrawingContext.cs | 7 +++--- .../Composition/Server/DrawingContextProxy.cs | 2 +- .../Server/ServerCompositionVisual.cs | 2 +- .../Rendering/ImmediateRenderer.cs | 2 +- .../HeadlessPlatformRenderInterface.cs | 2 +- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 14 ++++++++---- .../Media/DrawingContextImpl.cs | 15 ++++++++++--- .../Controls/CustomRenderTests.cs | 2 +- 13 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/Avalonia.Base/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs index 02294368c5..c55d6de10f 100644 --- a/src/Avalonia.Base/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -360,16 +360,15 @@ namespace Avalonia.Media /// Pushes an opacity value. /// /// The opacity. - /// The bounds. /// A disposable used to undo the opacity. - public PushedState PushOpacity(double opacity, Rect bounds) + public PushedState PushOpacity(double opacity) { - PushOpacityCore(opacity, bounds); + PushOpacityCore(opacity); _states ??= StateStackPool.Get(); _states.Push(new RestoreState(this, RestoreState.PushedStateType.Opacity)); return new PushedState(this); } - protected abstract void PushOpacityCore(double opacity, Rect bounds); + protected abstract void PushOpacityCore(double opacity); /// /// Pushes an opacity mask. diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs index dfcb5c1eb4..294d623c1e 100644 --- a/src/Avalonia.Base/Media/DrawingGroup.cs +++ b/src/Avalonia.Base/Media/DrawingGroup.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Avalonia.Media.Imaging; using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; @@ -72,9 +71,8 @@ namespace Avalonia.Media internal override void DrawCore(DrawingContext context) { var bounds = GetBounds(); - using (context.PushTransform(Transform?.Value ?? Matrix.Identity)) - using (context.PushOpacity(Opacity, bounds)) + using (context.PushOpacity(Opacity)) using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default) using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, bounds) : default) { @@ -178,22 +176,30 @@ namespace Avalonia.Media protected override void PushClipCore(Rect rect) { - throw new NotImplementedException(); + var drawingGroup = PushNewDrawingGroup(); + + drawingGroup.ClipGeometry = new RectangleGeometry(rect); } protected override void PushGeometryClipCore(Geometry clip) { - throw new NotImplementedException(); + var drawingGroup = PushNewDrawingGroup(); + + drawingGroup.ClipGeometry = clip; } - protected override void PushOpacityCore(double opacity, Rect bounds) + protected override void PushOpacityCore(double opacity) { - throw new NotImplementedException(); + var drawingGroup = PushNewDrawingGroup(); + + drawingGroup.Opacity = opacity; } protected override void PushOpacityMaskCore(IBrush mask, Rect bounds) { - throw new NotImplementedException(); + var drawingGroup = PushNewDrawingGroup(); + + drawingGroup.OpacityMask = mask; } internal override void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect) diff --git a/src/Avalonia.Base/Media/PlatformDrawingContext.cs b/src/Avalonia.Base/Media/PlatformDrawingContext.cs index 1c1f202532..b81582f845 100644 --- a/src/Avalonia.Base/Media/PlatformDrawingContext.cs +++ b/src/Avalonia.Base/Media/PlatformDrawingContext.cs @@ -76,8 +76,8 @@ internal sealed class PlatformDrawingContext : DrawingContext protected override void PushGeometryClipCore(Geometry clip) => _impl.PushGeometryClip(clip.PlatformImpl ?? throw new ArgumentException()); - protected override void PushOpacityCore(double opacity, Rect bounds) => - _impl.PushOpacity(opacity, bounds); + protected override void PushOpacityCore(double opacity) => + _impl.PushOpacity(opacity, null); protected override void PushOpacityMaskCore(IBrush mask, Rect bounds) => _impl.PushOpacityMask(mask, bounds); diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 8416354bc2..a0cd2a8dcd 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -132,7 +132,7 @@ namespace Avalonia.Platform /// /// The opacity. /// where to apply the opacity. - void PushOpacity(double opacity, Rect bounds); + void PushOpacity(double opacity, Rect? bounds); /// /// Pops the latest pushed opacity value. diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs index 3012432e12..2d3b5b0f22 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/Nodes/RenderDataNodes.cs @@ -182,11 +182,10 @@ class RenderDataGeometryClipNode : RenderDataPushNode class RenderDataOpacityNode : RenderDataPushNode { public double Opacity { get; set; } - public Rect BoundsRect { get; set; } public override void Push(ref RenderDataNodeRenderContext context) { if (Opacity != 1) - context.Context.PushOpacity(Opacity, BoundsRect); + context.Context.PushOpacity(Opacity, null); } public override void Pop(ref RenderDataNodeRenderContext context) @@ -211,4 +210,4 @@ abstract class RenderDataBrushAndPenNode : IRenderDataItemWithServerResources public abstract void Invoke(ref RenderDataNodeRenderContext context); public abstract Rect? Bounds { get; } public abstract bool HitTest(Point p); -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs b/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs index 3d5033086e..ef533e1662 100644 --- a/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs +++ b/src/Avalonia.Base/Rendering/Composition/Drawing/RenderDataDrawingContext.cs @@ -212,15 +212,14 @@ internal class RenderDataDrawingContext : DrawingContext }); } - protected override void PushOpacityCore(double opacity, Rect bounds) + protected override void PushOpacityCore(double opacity) { if (opacity == 1) Push(); else Push(new RenderDataOpacityNode { - Opacity = opacity, - BoundsRect = bounds + Opacity = opacity }); } @@ -346,4 +345,4 @@ internal class RenderDataDrawingContext : DrawingContext if (_resourcesHashSet != null) s_hashSetPool.ReturnAndSetNull(ref _resourcesHashSet); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs index b735b106d8..14dd59b7e3 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs @@ -109,7 +109,7 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, _impl.PopClip(); } - public void PushOpacity(double opacity, Rect bounds) + public void PushOpacity(double opacity, Rect? bounds) { _impl.PushOpacity(opacity, bounds); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 853b90be5e..0eeffda123 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -58,7 +58,7 @@ namespace Avalonia.Rendering.Composition.Server canvas.PushEffect(Effect); if (Opacity != 1) - canvas.PushOpacity(Opacity, boundsRect); + canvas.PushOpacity(Opacity, ClipToBounds ? boundsRect : null); if (ClipToBounds && !HandlesClipToBounds) canvas.PushClip(Root!.SnapToDevicePixels(boundsRect)); if (Clip != null) diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index 3462b1008a..37c5e0a2c6 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -96,7 +96,7 @@ namespace Avalonia.Rendering } using (context.PushTransform(m)) - using (context.PushOpacity(opacity, bounds)) + using (context.PushOpacity(opacity)) using (clipToBounds #pragma warning disable CS0618 // Type or member is obsolete ? visual is IVisualWithRoundRectClip roundClipVisual diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index f99bebb5fe..7293874671 100644 --- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -457,7 +457,7 @@ namespace Avalonia.Headless } - public void PushOpacity(double opacity, Rect rect) + public void PushOpacity(double opacity, Rect? rect) { } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 18cb87dfd1..56e0877708 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -7,7 +7,6 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering.Utilities; using Avalonia.Utilities; -using Avalonia.Media.Imaging; using SkiaSharp; using ISceneBrush = Avalonia.Media.ISceneBrush; @@ -573,14 +572,21 @@ namespace Avalonia.Skia } /// - public void PushOpacity(double opacity, Rect bounds) + public void PushOpacity(double opacity, Rect? bounds) { CheckLease(); if(_useOpacitySaveLayer) { - var rect = bounds.ToSKRect(); - Canvas.SaveLayer(rect, new SKPaint { ColorF = new SKColorF(0, 0, 0, (float)opacity)}); + if (bounds.HasValue) + { + var rect = bounds.Value.ToSKRect(); + Canvas.SaveLayer(rect, new SKPaint { ColorF = new SKColorF(0, 0, 0, (float)opacity) }); + } + else + { + Canvas.SaveLayer(new SKPaint { ColorF = new SKColorF(0, 0, 0, (float)opacity) }); + } } else { diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 407fadb9f5..5579278f21 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -454,17 +454,26 @@ namespace Avalonia.Direct2D1.Media /// The opacity. /// The bounds. /// A disposable used to undo the opacity. - public void PushOpacity(double opacity, Rect bounds) + public void PushOpacity(double opacity, Rect? bounds) { if (opacity < 1) { + if(bounds == null || bounds == default(Rect)) + { + bounds = new Rect(0, 0, _renderTarget.PixelSize.Width, _renderTarget.PixelSize.Height); + } + var parameters = new LayerParameters { - ContentBounds = bounds.ToDirect2D(), MaskTransform = PrimitiveExtensions.Matrix3x2Identity, - Opacity = (float)opacity, + Opacity = (float)opacity }; + if(bounds.HasValue) + { + parameters.ContentBounds = bounds.Value.ToDirect2D(); + } + var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_deviceContext); _deviceContext.PushLayer(ref parameters, layer); diff --git a/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs b/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs index 7a2c60baf4..ff36877d9a 100644 --- a/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs +++ b/tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs @@ -141,7 +141,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls new Rect(control.Bounds.Size), 4); - using (context.PushOpacity(0.5, control.Bounds)) + using (context.PushOpacity(0.5)) { context.FillRectangle( Brushes.Blue,