diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index d7870b97614..fed3b83aafd 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -37,6 +37,18 @@ baseline/netstandard2.0/Avalonia.Base.dll target/netstandard2.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Platform.IDrawingContextImplWithEffects.PopEffect + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll + + + CP0002 + M:Avalonia.Platform.IDrawingContextImplWithEffects.PushEffect(Avalonia.Media.IEffect) + baseline/netstandard2.0/Avalonia.Base.dll + target/netstandard2.0/Avalonia.Base.dll + CP0002 M:Avalonia.Controls.Primitives.IPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect}) diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 476bca5a33e..1a813f7bbe7 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -201,9 +201,10 @@ void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, object? GetFeature(Type t); } - public interface IDrawingContextImplWithEffects + [PrivateApi] + public interface IDrawingContextImplWithEffects : IDrawingContextImpl { - void PushEffect(IEffect effect); + void PushEffect(Rect? clipRect, IEffect effect); void PopEffect(); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs index 74389903f4f..ee0447629a6 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.PendingCommands.cs @@ -48,6 +48,10 @@ struct PendingCommandDataUnion [FieldOffset(0)] public bool IsRoundRect; [FieldOffset(4)] public RoundedRect RoundRect; [FieldOffset(4)] public Rect NormalRect; + + // PushEffect + [FieldOffset(0)] + public Rect? EffectClipRect; } struct PendingCommand @@ -140,7 +144,7 @@ void ExecCommand(ref PendingCommand cmd) else if (cmd.Type == PendingCommandType.PushEffect) { if (_impl is IDrawingContextImplWithEffects effects) - effects.PushEffect(cmd.ObjectUnion.Effect!); + effects.PushEffect(cmd.DataUnion.EffectClipRect, cmd.ObjectUnion.Effect!); } else if (cmd.Type == PendingCommandType.PushRenderOptions) _impl.PushRenderOptions(cmd.DataUnion.RenderOptions); diff --git a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs index 12ee8ad41c5..6b4982c4903 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs @@ -285,14 +285,18 @@ public void DrawRectangle(IExperimentalAcrylicMaterial material, RoundedRect rec _impl.DrawRectangle(new ImmutableSolidColorBrush(material.FallbackColor), null, rect); } - public void PushEffect(IEffect effect) + public void PushEffect(Rect? clipRect, IEffect effect) { AddCommand(new() { Type = PendingCommandType.PushEffect, ObjectUnion = { - Effect = effect + Effect = effect, + }, + DataUnion = + { + EffectClipRect = clipRect } }); } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs index 4f300503b2f..396009841b9 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs @@ -65,6 +65,8 @@ public override UpdateResult Update(ServerCompositionTarget root, Matrix parentC return new(_transformedContentBounds, oldInvalidated, newInvalidated); } + protected override LtrbRect GetEffectBounds() => _transformedContentBounds ?? default; + void AddEffectPaddedDirtyRect(IImmutableEffect effect, LtrbRect transformedBounds) { var padding = effect.GetEffectOutputPadding(); diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index 112a817541c..06ada8157d0 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -62,9 +62,8 @@ public void Render(ServerVisualRenderContext context, LtrbRect? parentTransforme if (applyRenderOptions) canvas.PushRenderOptions(RenderOptions); - if (Effect != null) - canvas.PushEffect(Effect); - + var needPopEffect = PushEffect(canvas); + if (Opacity != 1) canvas.PushOpacity(Opacity, ClipToBounds ? boundsRect : null); if (ClipToBounds && !HandlesClipToBounds) @@ -87,12 +86,28 @@ public void Render(ServerVisualRenderContext context, LtrbRect? parentTransforme if (Opacity != 1) canvas.PopOpacity(); - if (Effect != null) + if (needPopEffect) canvas.PopEffect(); if(applyRenderOptions) canvas.PopRenderOptions(); } + protected virtual LtrbRect GetEffectBounds() => TransformedOwnContentBounds; + + private bool PushEffect(CompositorDrawingContextProxy canvas) + { + if (Effect == null) + return false; + var clip = GetEffectBounds(); + if (clip.IsZeroSize) + return false; + var oldMatrix = canvas.Transform; + canvas.Transform = Matrix.Identity; + canvas.PushEffect(GetEffectBounds().ToRect(), Effect!); + canvas.Transform = oldMatrix; + return true; + } + protected virtual bool HandlesClipToBounds => false; private ReadbackData _readback0, _readback1, _readback2; diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs index ab528c326bc..7621e8436c3 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.Effects.cs @@ -7,13 +7,16 @@ namespace Avalonia.Skia; partial class DrawingContextImpl { - public void PushEffect(IEffect effect) + public void PushEffect(Rect? effectClipRect, IEffect effect) { CheckLease(); using var filter = CreateEffect(effect); var paint = SKPaintCache.Shared.Get(); paint.ImageFilter = filter; - Canvas.SaveLayer(paint); + if (effectClipRect.HasValue) + Canvas.SaveLayer(effectClipRect.Value.ToSKRect(), paint); + else + Canvas.SaveLayer(paint); SKPaintCache.Shared.ReturnReset(paint); }