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