Browse Source

Fix visual snapshot on headless (#17565)

* Check for HasRenderContextAffinity before calling CreateNonAffinedSnapshot

* Add ServerVisualRenderContext.RenderChildren context parameter

* Add Should_Render_To_A_Compositor_Snapshot_Capture test

* Fix xunit tests
release/11.2.2
Max Katz 1 year ago
parent
commit
fe28a3f60f
  1. 2
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  2. 7
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs
  3. 2
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  4. 9
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.UserApis.cs
  5. 6
      src/Avalonia.Base/Rendering/Composition/Server/ServerVisualRenderContext.cs
  6. 36
      tests/Avalonia.Headless.UnitTests/RenderingTests.cs

2
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@ -294,7 +294,7 @@ namespace Avalonia.Rendering.Composition
throw new InvalidOperationException();
if (visual.Root == null)
throw new InvalidOperationException();
var impl = await InvokeServerJobAsync(() => _server.CreateCompositionVisualSnapshot(visual.Server, scaling), true);
var impl = await InvokeServerJobAsync(() => _server.CreateCompositionVisualSnapshot(visual.Server, scaling, true), true);
return new Bitmap(RefCountable.Create(impl));
}

7
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionContainerVisual.cs

@ -20,9 +20,12 @@ namespace Avalonia.Rendering.Composition.Server
{
base.RenderCore(context, currentTransformedClip);
foreach (var ch in Children)
if (context.RenderChildren)
{
ch.Render(context, currentTransformedClip);
foreach (var ch in Children)
{
ch.Render(context, currentTransformedClip);
}
}
}

2
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@ -215,7 +215,7 @@ namespace Avalonia.Rendering.Composition.Server
using (var proxy = new CompositorDrawingContextProxy(context))
{
var ctx = new ServerVisualRenderContext(proxy, DirtyRects, false);
var ctx = new ServerVisualRenderContext(proxy, DirtyRects, false, true);
root.Render(ctx, null);
}

9
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.UserApis.cs

@ -49,7 +49,7 @@ internal partial class ServerCompositor
}
public IBitmapImpl CreateCompositionVisualSnapshot(ServerCompositionVisual visual,
double scaling)
double scaling, bool renderChildren)
{
using (RenderInterface.EnsureCurrent())
{
@ -69,11 +69,12 @@ internal partial class ServerCompositor
PostTransform = invertRootTransform * scaleTransform,
Transform = Matrix.Identity
};
var ctx = new ServerVisualRenderContext(proxy, null, true);
var ctx = new ServerVisualRenderContext(proxy, null, true, renderChildren);
visual.Render(ctx, null);
}
if (target is IDrawingContextLayerWithRenderContextAffinityImpl affined)
if (target is IDrawingContextLayerWithRenderContextAffinityImpl affined
&& affined.HasRenderContextAffinity)
return affined.CreateNonAffinedSnapshot();
// We are returning the original target, so prevent it from being disposed
@ -87,4 +88,4 @@ internal partial class ServerCompositor
}
}
}
}
}

6
src/Avalonia.Base/Rendering/Composition/Server/ServerVisualRenderContext.cs

@ -8,15 +8,17 @@ internal class ServerVisualRenderContext
{
public IDirtyRectTracker? DirtyRects { get; }
public bool DetachedRendering { get; }
public bool RenderChildren { get; }
public CompositorDrawingContextProxy Canvas { get; }
private readonly Stack<Matrix>? _transformStack;
public ServerVisualRenderContext(CompositorDrawingContextProxy canvas, IDirtyRectTracker? dirtyRects,
bool detachedRendering)
bool detachedRendering, bool renderChildren)
{
Canvas = canvas;
DirtyRects = dirtyRects;
DetachedRendering = detachedRendering;
RenderChildren = renderChildren;
if (detachedRendering)
{
_transformStack = new();
@ -72,4 +74,4 @@ internal class ServerVisualRenderContext
}
}
}
}

36
tests/Avalonia.Headless.UnitTests/RenderingTests.cs

@ -1,8 +1,10 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;
namespace Avalonia.Headless.UnitTests;
@ -37,7 +39,7 @@ public class RenderingTests
Assert.NotNull(frame);
}
#if NUNIT
[AvaloniaTest, Timeout(10000)]
#elif XUNIT
@ -136,4 +138,36 @@ public class RenderingTests
var frame = window.CaptureRenderedFrame();
Assert.NotNull(frame);
}
#if NUNIT
[AvaloniaTest, Timeout(10000)]
#elif XUNIT
[AvaloniaFact(Timeout = 10000)]
#endif
public async Task Should_Render_To_A_Compositor_Snapshot_Capture()
{
var window = new Window
{
Content = new ContentControl
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch,
Width = 100,
Height = 100,
Background = Brushes.Green
},
SizeToContent = SizeToContent.WidthAndHeight
};
window.Show();
var compositionVisual = ElementComposition.GetElementVisual(window)!;
var snapshot = await compositionVisual.Compositor.CreateCompositionVisualSnapshot(compositionVisual, 1);
Assert.NotNull(snapshot);
// ReSharper disable CompareOfFloatsByEqualityOperator
Assert.True(100 == snapshot.Size.Width);
Assert.True(100 == snapshot.Size.Height);
// ReSharper restore CompareOfFloatsByEqualityOperator
}
}

Loading…
Cancel
Save