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; 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; Visual = visual;
} }

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

@ -1,5 +1,6 @@
using System; using System;
using Avalonia.Collections.Pooled; using Avalonia.Collections.Pooled;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -7,6 +8,8 @@ namespace Avalonia.Rendering.Composition.Drawing;
internal class CompositionDrawList : PooledList<IRef<IDrawOperation>> internal class CompositionDrawList : PooledList<IRef<IDrawOperation>>
{ {
public Size? Size { get; set; }
public CompositionDrawList() public CompositionDrawList()
{ {
@ -26,11 +29,22 @@ internal class CompositionDrawList : PooledList<IRef<IDrawOperation>>
public CompositionDrawList Clone() public CompositionDrawList Clone()
{ {
var clone = new CompositionDrawList(Count); var clone = new CompositionDrawList(Count) { Size = Size };
foreach (var r in this) foreach (var r in this)
clone.Add(r.Clone()); clone.Add(r.Clone());
return 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 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> ? _builder.DrawOperations[_drawOperationIndex] as IRef<T>
: null; : null;
} }
private IDictionary<IVisual, Scene>? CreateChildScene(IBrush? brush) private IDisposable? CreateChildScene(IBrush? brush)
{ {
/* if (brush is VisualBrush visualBrush)
var visualBrush = brush as VisualBrush;
if (visualBrush != null)
{ {
var visual = visualBrush.Visual; var visual = visualBrush.Visual;
if (visual != null) 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(); (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; return null;
} }
} }

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

@ -2,6 +2,7 @@ using System.Numerics;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.Composition.Drawing;
using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -10,12 +11,21 @@ namespace Avalonia.Rendering.Composition.Server;
internal class CompositorDrawingContextProxy : IDrawingContextImpl internal class CompositorDrawingContextProxy : IDrawingContextImpl
{ {
private IDrawingContextImpl _impl; private IDrawingContextImpl _impl;
private readonly VisualBrushRenderer _visualBrushRenderer;
public CompositorDrawingContextProxy(IDrawingContextImpl impl) public CompositorDrawingContextProxy(IDrawingContextImpl impl, VisualBrushRenderer visualBrushRenderer)
{ {
_impl = impl; _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 Matrix PreTransform { get; set; } = Matrix.Identity;
public void Dispose() public void Dispose()
@ -135,4 +145,22 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl
{ {
_impl.Custom(custom); _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) if (_renderCommands != null)
{ {
foreach (var cmd in _renderCommands) _renderCommands.Render(canvas);
{
cmd.Item.Render(canvas);
}
} }
base.RenderCore(canvas, transform); 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) if (!_dirtyRect.IsEmpty)
{ {
using (var context = _layer.CreateDrawingContext(null)) var visualBrushHelper = new CompositorDrawingContextProxy.VisualBrushRenderer();
using (var context = _layer.CreateDrawingContext(visualBrushHelper))
{ {
context.PushClip(_dirtyRect); context.PushClip(_dirtyRect);
context.Clear(Colors.Transparent); context.Clear(Colors.Transparent);
Root.Render(new CompositorDrawingContextProxy(context), Root.CombinedTransformMatrix); Root.Render(new CompositorDrawingContextProxy(context, visualBrushHelper), Root.CombinedTransformMatrix);
context.PopClip(); 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/> /// <inheritdoc/>
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush) Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{ {
return (_currentDraw?.Item as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty; return TryGetChildScene(_currentDraw)?.Size ?? Size.Empty;
} }
/// <inheritdoc/> /// <inheritdoc/>
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush) void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{ {
var childScene = (_currentDraw?.Item as BrushDrawOperation)?.ChildScenes?[brush.Visual]; var childScene = TryGetChildScene(_currentDraw);
if (childScene != null) 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.Media;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -9,14 +10,21 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
internal abstract class BrushDrawOperation : DrawOperation internal abstract class BrushDrawOperation : DrawOperation
{ {
public BrushDrawOperation(Rect bounds, Matrix transform) public BrushDrawOperation(Rect bounds, Matrix transform, IDisposable? aux)
: base(bounds, transform) : base(bounds, transform)
{ {
Aux = aux;
} }
/// <summary> /// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes. /// Auxiliary data required to draw the brush
/// </summary> /// </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; 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; var visualBrush = brush as VisualBrush;
@ -470,7 +470,7 @@ namespace Avalonia.Rendering.SceneGraph
(visual as IVisualBrushInitialize)?.EnsureInitialized(); (visual as IVisualBrushInitialize)?.EnsureInitialized();
var scene = new Scene(visual); var scene = new Scene(visual);
_sceneBuilder.UpdateAll(scene); _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, IBrush? brush,
IPen? pen, IPen? pen,
Rect rect, Rect rect,
IDictionary<IVisual, Scene>? childScenes = null) IDisposable? aux = null)
: base(rect.Inflate(pen?.Thickness ?? 0), transform) : base(rect.Inflate(pen?.Thickness ?? 0), transform, aux)
{ {
Transform = transform; Transform = transform;
Brush = brush?.ToImmutable(); Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
Rect = rect; Rect = rect;
ChildScenes = childScenes;
} }
/// <summary> /// <summary>
@ -47,8 +46,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public Rect Rect { get; } public Rect Rect { get; }
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
public bool Equals(Matrix transform, IBrush? brush, IPen? pen, Rect rect) public bool Equals(Matrix transform, IBrush? brush, IPen? pen, Rect rect)
{ {
return transform == Transform && 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;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Platform; using Avalonia.Platform;
@ -23,14 +24,13 @@ namespace Avalonia.Rendering.SceneGraph
IBrush? brush, IBrush? brush,
IPen? pen, IPen? pen,
IGeometryImpl geometry, IGeometryImpl geometry,
IDictionary<IVisual, Scene>? childScenes = null) IDisposable? aux)
: base(geometry.GetRenderBounds(pen).CalculateBoundsWithLineCaps(pen), transform) : base(geometry.GetRenderBounds(pen).CalculateBoundsWithLineCaps(pen), transform, aux)
{ {
Transform = transform; Transform = transform;
Brush = brush?.ToImmutable(); Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
Geometry = geometry; Geometry = geometry;
ChildScenes = childScenes;
} }
/// <summary> /// <summary>
@ -53,9 +53,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public IGeometryImpl Geometry { get; } public IGeometryImpl Geometry { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <summary> /// <summary>
/// Determines if this draw operation equals another. /// Determines if this draw operation equals another.
/// </summary> /// </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;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
@ -23,13 +24,12 @@ namespace Avalonia.Rendering.SceneGraph
Matrix transform, Matrix transform,
IBrush foreground, IBrush foreground,
GlyphRun glyphRun, GlyphRun glyphRun,
IDictionary<IVisual, Scene>? childScenes = null) IDisposable? aux = null)
: base(new Rect(glyphRun.Size), transform) : base(new Rect(glyphRun.Size), transform, aux)
{ {
Transform = transform; Transform = transform;
Foreground = foreground.ToImmutable(); Foreground = foreground.ToImmutable();
GlyphRun = glyphRun; GlyphRun = glyphRun;
ChildScenes = childScenes;
} }
/// <summary> /// <summary>
@ -47,9 +47,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public GlyphRun GlyphRun { get; } public GlyphRun GlyphRun { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override void Render(IDrawingContextImpl context) public override void Render(IDrawingContextImpl context)
{ {

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

@ -25,14 +25,13 @@ namespace Avalonia.Rendering.SceneGraph
IPen pen, IPen pen,
Point p1, Point p1,
Point p2, Point p2,
IDictionary<IVisual, Scene>? childScenes = null) IDisposable? aux = null)
: base(LineBoundsHelper.CalculateBounds(p1, p2, pen), transform) : base(LineBoundsHelper.CalculateBounds(p1, p2, pen), transform, aux)
{ {
Transform = transform; Transform = transform;
Pen = pen.ToImmutable(); Pen = pen.ToImmutable();
P1 = p1; P1 = p1;
P2 = p2; P2 = p2;
ChildScenes = childScenes;
} }
/// <summary> /// <summary>
@ -55,9 +54,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public Point P2 { get; } public Point P2 { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <summary> /// <summary>
/// Determines if this draw operation equals another. /// Determines if this draw operation equals another.
/// </summary> /// </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.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -17,12 +18,11 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="mask">The opacity mask to push.</param> /// <param name="mask">The opacity mask to push.</param>
/// <param name="bounds">The bounds of the mask.</param> /// <param name="bounds">The bounds of the mask.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param> /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary<IVisual, Scene>? childScenes = null) public OpacityMaskNode(IBrush mask, Rect bounds, IDisposable? aux = null)
: base(Rect.Empty, Matrix.Identity) : base(Rect.Empty, Matrix.Identity, aux)
{ {
Mask = mask.ToImmutable(); Mask = mask.ToImmutable();
MaskBounds = bounds; MaskBounds = bounds;
ChildScenes = childScenes;
} }
/// <summary> /// <summary>
@ -30,7 +30,7 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity mask pop. /// opacity mask pop.
/// </summary> /// </summary>
public OpacityMaskNode() public OpacityMaskNode()
: base(Rect.Empty, Matrix.Identity) : base(Rect.Empty, Matrix.Identity, null)
{ {
} }
@ -44,8 +44,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public Rect? MaskBounds { get; } public Rect? MaskBounds { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override bool HitTest(Point p) => false; 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;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Platform; using Avalonia.Platform;
@ -26,14 +27,13 @@ namespace Avalonia.Rendering.SceneGraph
IPen? pen, IPen? pen,
RoundedRect rect, RoundedRect rect,
BoxShadows boxShadows, BoxShadows boxShadows,
IDictionary<IVisual, Scene>? childScenes = null) IDisposable? aux = null)
: base(boxShadows.TransformBounds(rect.Rect).Inflate((pen?.Thickness ?? 0) / 2), transform) : base(boxShadows.TransformBounds(rect.Rect).Inflate((pen?.Thickness ?? 0) / 2), transform, aux)
{ {
Transform = transform; Transform = transform;
Brush = brush?.ToImmutable(); Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable(); Pen = pen?.ToImmutable();
Rect = rect; Rect = rect;
ChildScenes = childScenes;
BoxShadows = boxShadows; BoxShadows = boxShadows;
} }
@ -62,9 +62,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public BoxShadows BoxShadows { get; } public BoxShadows BoxShadows { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene>? ChildScenes { get; }
/// <summary> /// <summary>
/// Determines if this draw operation equals another. /// Determines if this draw operation equals another.
/// </summary> /// </summary>

Loading…
Cancel
Save