Browse Source

Added VisualBrush support

pull/8105/head
Nikita Tsukanov 4 years ago
parent
commit
bcbd86eca3
  1. 2
      src/Avalonia.Base/Rendering/Composition/CompositionDrawListVisual.cs
  2. 16
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawList.cs
  3. 27
      src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs
  4. 30
      src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs
  5. 5
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs
  6. 5
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  7. 6
      src/Avalonia.Base/Rendering/DeferredRenderer.cs
  8. 16
      src/Avalonia.Base/Rendering/SceneGraph/BrushDrawOperation.cs
  9. 4
      src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  10. 7
      src/Avalonia.Base/Rendering/SceneGraph/EllipseNode.cs
  11. 11
      src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs
  12. 11
      src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs
  13. 8
      src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs
  14. 12
      src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs
  15. 11
      src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs

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

@ -24,7 +24,7 @@ internal class CompositionDrawListVisual : CompositionContainerVisual
private protected override IChangeSetPool ChangeSetPool => DrawListVisualChanges.Pool;
internal CompositionDrawListVisual(Compositor compositor, ServerCompositionContainerVisual server, Visual visual) : base(compositor, server)
internal CompositionDrawListVisual(Compositor compositor, ServerCompositionDrawListVisual server, Visual visual) : base(compositor, server)
{
Visual = visual;
}

16
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawList.cs

@ -1,5 +1,6 @@
using System;
using Avalonia.Collections.Pooled;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
@ -7,6 +8,8 @@ namespace Avalonia.Rendering.Composition.Drawing;
internal class CompositionDrawList : PooledList<IRef<IDrawOperation>>
{
public Size? Size { get; set; }
public CompositionDrawList()
{
@ -26,11 +29,22 @@ internal class CompositionDrawList : PooledList<IRef<IDrawOperation>>
public CompositionDrawList Clone()
{
var clone = new CompositionDrawList(Count);
var clone = new CompositionDrawList(Count) { Size = Size };
foreach (var r in this)
clone.Add(r.Clone());
return clone;
}
public void Render(CompositorDrawingContextProxy canvas)
{
foreach (var cmd in this)
{
canvas.VisualBrushDrawList = (cmd.Item as BrushDrawOperation)?.Aux as CompositionDrawList;
cmd.Item.Render(canvas);
}
canvas.VisualBrushDrawList = null;
}
}
internal class CompositionDrawListBuilder

27
src/Avalonia.Base/Rendering/Composition/Drawing/CompositionDrawingContext.cs

@ -359,25 +359,30 @@ internal class CompositionDrawingContext : IDrawingContextImpl
? _builder.DrawOperations[_drawOperationIndex] as IRef<T>
: null;
}
private IDictionary<IVisual, Scene>? CreateChildScene(IBrush? brush)
private IDisposable? CreateChildScene(IBrush? brush)
{
/*
var visualBrush = brush as VisualBrush;
if (visualBrush != null)
if (brush is VisualBrush visualBrush)
{
var visual = visualBrush.Visual;
if (visual != null)
{
// TODO: This is a temporary solution to make visual brush to work like it does with DeferredRenderer
// We should directly reference the corresponding CompositionVisual (which should
// be attached to the same composition target) like UWP does.
// Render-able visuals shouldn't be dangling unattached
(visual as IVisualBrushInitialize)?.EnsureInitialized();
var scene = new Scene(visual);
_sceneBuilder.UpdateAll(scene);
return new Dictionary<IVisual, Scene> { { visualBrush.Visual, scene } };
}
}*/
var drawList = new CompositionDrawList() { Size = visual.Bounds.Size };
var recorder = new CompositionDrawingContext();
recorder.BeginUpdate(drawList);
ImmediateRenderer.Render(visual, new DrawingContext(recorder));
recorder.EndUpdate();
return drawList;
}
}
return null;
}
}

30
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs

@ -2,6 +2,7 @@ using System.Numerics;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering.Composition.Drawing;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
@ -10,12 +11,21 @@ namespace Avalonia.Rendering.Composition.Server;
internal class CompositorDrawingContextProxy : IDrawingContextImpl
{
private IDrawingContextImpl _impl;
private readonly VisualBrushRenderer _visualBrushRenderer;
public CompositorDrawingContextProxy(IDrawingContextImpl impl)
public CompositorDrawingContextProxy(IDrawingContextImpl impl, VisualBrushRenderer visualBrushRenderer)
{
_impl = impl;
_visualBrushRenderer = visualBrushRenderer;
}
// This is a hack to make it work with the current way of handling visual brushes
public CompositionDrawList? VisualBrushDrawList
{
get => _visualBrushRenderer.VisualBrushDrawList;
set => _visualBrushRenderer.VisualBrushDrawList = value;
}
public Matrix PreTransform { get; set; } = Matrix.Identity;
public void Dispose()
@ -135,4 +145,22 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl
{
_impl.Custom(custom);
}
public class VisualBrushRenderer : IVisualBrushRenderer
{
public CompositionDrawList? VisualBrushDrawList { get; set; }
public Size GetRenderTargetSize(IVisualBrush brush)
{
return VisualBrushDrawList?.Size ?? Size.Empty;
}
public void RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{
if (VisualBrushDrawList != null)
{
foreach (var cmd in VisualBrushDrawList)
cmd.Item.Render(context);
}
}
}
}

5
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawListVisual.cs

@ -51,10 +51,7 @@ internal class ServerCompositionDrawListVisual : ServerCompositionContainerVisua
{
if (_renderCommands != null)
{
foreach (var cmd in _renderCommands)
{
cmd.Item.Render(canvas);
}
_renderCommands.Render(canvas);
}
base.RenderCore(canvas, transform);
}

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

@ -75,11 +75,12 @@ namespace Avalonia.Rendering.Composition.Server
if (!_dirtyRect.IsEmpty)
{
using (var context = _layer.CreateDrawingContext(null))
var visualBrushHelper = new CompositorDrawingContextProxy.VisualBrushRenderer();
using (var context = _layer.CreateDrawingContext(visualBrushHelper))
{
context.PushClip(_dirtyRect);
context.Clear(Colors.Transparent);
Root.Render(new CompositorDrawingContextProxy(context), Root.CombinedTransformMatrix);
Root.Render(new CompositorDrawingContextProxy(context, visualBrushHelper), Root.CombinedTransformMatrix);
context.PopClip();
}
}

6
src/Avalonia.Base/Rendering/DeferredRenderer.cs

@ -272,16 +272,18 @@ namespace Avalonia.Rendering
}
}
Scene? TryGetChildScene(IRef<IDrawOperation>? op) => (op?.Item as BrushDrawOperation)?.Aux as Scene;
/// <inheritdoc/>
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{
return (_currentDraw?.Item as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty;
return TryGetChildScene(_currentDraw)?.Size ?? Size.Empty;
}
/// <inheritdoc/>
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{
var childScene = (_currentDraw?.Item as BrushDrawOperation)?.ChildScenes?[brush.Visual];
var childScene = TryGetChildScene(_currentDraw);
if (childScene != null)
{

16
src/Avalonia.Base/Rendering/SceneGraph/BrushDrawOperation.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.VisualTree;
@ -9,14 +10,21 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
internal abstract class BrushDrawOperation : DrawOperation
{
public BrushDrawOperation(Rect bounds, Matrix transform)
public BrushDrawOperation(Rect bounds, Matrix transform, IDisposable? aux)
: base(bounds, transform)
{
Aux = aux;
}
/// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes.
/// Auxiliary data required to draw the brush
/// </summary>
public abstract IDictionary<IVisual, Scene>? ChildScenes { get; }
public IDisposable? Aux { get; }
public override void Dispose()
{
Aux?.Dispose();
base.Dispose();
}
}
}

4
src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -457,7 +457,7 @@ namespace Avalonia.Rendering.SceneGraph
return _drawOperationindex < _node!.DrawOperations.Count ? _node.DrawOperations[_drawOperationindex] as IRef<T> : null;
}
private IDictionary<IVisual, Scene>? CreateChildScene(IBrush? brush)
private IDisposable? CreateChildScene(IBrush? brush)
{
var visualBrush = brush as VisualBrush;
@ -470,7 +470,7 @@ namespace Avalonia.Rendering.SceneGraph
(visual as IVisualBrushInitialize)?.EnsureInitialized();
var scene = new Scene(visual);
_sceneBuilder.UpdateAll(scene);
return new Dictionary<IVisual, Scene> { { visualBrush.Visual, scene } };
return scene;
}
}

7
src/Avalonia.Base/Rendering/SceneGraph/EllipseNode.cs

@ -17,14 +17,13 @@ namespace Avalonia.Rendering.SceneGraph
IBrush? brush,
IPen? pen,
Rect rect,
IDictionary<IVisual, Scene>? childScenes = null)
: base(rect.Inflate(pen?.Thickness ?? 0), transform)
IDisposable? aux = null)
: base(rect.Inflate(pen?.Thickness ?? 0), transform, aux)
{
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
Rect = rect;
ChildScenes = childScenes;
}
/// <summary>
@ -47,8 +46,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public Rect Rect { get; }
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
public bool Equals(Matrix transform, IBrush? brush, IPen? pen, Rect rect)
{
return transform == Transform &&

11
src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
@ -23,14 +24,13 @@ namespace Avalonia.Rendering.SceneGraph
IBrush? brush,
IPen? pen,
IGeometryImpl geometry,
IDictionary<IVisual, Scene>? childScenes = null)
: base(geometry.GetRenderBounds(pen).CalculateBoundsWithLineCaps(pen), transform)
IDisposable? aux)
: base(geometry.GetRenderBounds(pen).CalculateBoundsWithLineCaps(pen), transform, aux)
{
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
Geometry = geometry;
ChildScenes = childScenes;
}
/// <summary>
@ -53,9 +53,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public IGeometryImpl Geometry { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>

11
src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Immutable;
@ -23,13 +24,12 @@ namespace Avalonia.Rendering.SceneGraph
Matrix transform,
IBrush foreground,
GlyphRun glyphRun,
IDictionary<IVisual, Scene>? childScenes = null)
: base(new Rect(glyphRun.Size), transform)
IDisposable? aux = null)
: base(new Rect(glyphRun.Size), transform, aux)
{
Transform = transform;
Foreground = foreground.ToImmutable();
GlyphRun = glyphRun;
ChildScenes = childScenes;
}
/// <summary>
@ -47,9 +47,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public GlyphRun GlyphRun { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <inheritdoc/>
public override void Render(IDrawingContextImpl context)
{

8
src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs

@ -25,14 +25,13 @@ namespace Avalonia.Rendering.SceneGraph
IPen pen,
Point p1,
Point p2,
IDictionary<IVisual, Scene>? childScenes = null)
: base(LineBoundsHelper.CalculateBounds(p1, p2, pen), transform)
IDisposable? aux = null)
: base(LineBoundsHelper.CalculateBounds(p1, p2, pen), transform, aux)
{
Transform = transform;
Pen = pen.ToImmutable();
P1 = p1;
P2 = p2;
ChildScenes = childScenes;
}
/// <summary>
@ -55,9 +54,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public Point P2 { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>

12
src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
@ -17,12 +18,11 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="mask">The opacity mask to push.</param>
/// <param name="bounds">The bounds of the mask.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary<IVisual, Scene>? childScenes = null)
: base(Rect.Empty, Matrix.Identity)
public OpacityMaskNode(IBrush mask, Rect bounds, IDisposable? aux = null)
: base(Rect.Empty, Matrix.Identity, aux)
{
Mask = mask.ToImmutable();
MaskBounds = bounds;
ChildScenes = childScenes;
}
/// <summary>
@ -30,7 +30,7 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity mask pop.
/// </summary>
public OpacityMaskNode()
: base(Rect.Empty, Matrix.Identity)
: base(Rect.Empty, Matrix.Identity, null)
{
}
@ -44,8 +44,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public Rect? MaskBounds { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <inheritdoc/>
public override bool HitTest(Point p) => false;

11
src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
@ -26,14 +27,13 @@ namespace Avalonia.Rendering.SceneGraph
IPen? pen,
RoundedRect rect,
BoxShadows boxShadows,
IDictionary<IVisual, Scene>? childScenes = null)
: base(boxShadows.TransformBounds(rect.Rect).Inflate((pen?.Thickness ?? 0) / 2), transform)
IDisposable? aux = null)
: base(boxShadows.TransformBounds(rect.Rect).Inflate((pen?.Thickness ?? 0) / 2), transform, aux)
{
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
Rect = rect;
ChildScenes = childScenes;
BoxShadows = boxShadows;
}
@ -62,9 +62,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public BoxShadows BoxShadows { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>

Loading…
Cancel
Save