Browse Source

Added some docs.

And a few missing child scene properties.
scenegraph-after-breakage
Steven Kirk 9 years ago
parent
commit
be20562426
  1. 38
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  2. 26
      src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
  3. 51
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  4. 44
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  5. 16
      src/Avalonia.Visuals/Rendering/SceneGraph/ISceneBuilder.cs
  6. 50
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  7. 52
      src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
  8. 50
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  9. 54
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  10. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  11. 39
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs
  12. 63
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs
  13. 54
      src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs

38
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -14,6 +14,10 @@ using System.Threading;
namespace Avalonia.Rendering
{
/// <summary>
/// A renderer which renders the state of the visual tree to an intermediate scene graph
/// representation which is then rendered on a rendering thread.
/// </summary>
public class DeferredRenderer : RendererBase, IRenderer, IVisualBrushRenderer
{
private readonly IDispatcher _dispatcher;
@ -33,6 +37,14 @@ namespace Avalonia.Rendering
private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
private IDrawOperation _currentDraw;
/// <summary>
/// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
/// </summary>
/// <param name="root">The control to render.</param>
/// <param name="renderLoop">The render loop.</param>
/// <param name="sceneBuilder">The scene builder to use. Optional.</param>
/// <param name="layerFactory">The layer factory to use. Optional.</param>
/// <param name="dispatcher">The dispatcher to use. Optional.</param>
public DeferredRenderer(
IRenderRoot root,
IRenderLoop renderLoop,
@ -56,6 +68,16 @@ namespace Avalonia.Rendering
}
}
/// <summary>
/// Initializes a new instance of the <see cref="DeferredRenderer"/> class.
/// </summary>
/// <param name="root">The control to render.</param>
/// <param name="renderTarget">The render target.</param>
/// <param name="sceneBuilder">The scene builder to use. Optional.</param>
/// <param name="layerFactory">The layer factory to use. Optional.</param>
/// <remarks>
/// This constructor is intended to be used for unit testing.
/// </remarks>
public DeferredRenderer(
IVisual root,
IRenderTarget renderTarget,
@ -73,15 +95,26 @@ namespace Avalonia.Rendering
_layers = new RenderLayers(_layerFactory);
}
/// <inheritdoc/>
public bool DrawFps { get; set; }
/// <inheritdoc/>
public bool DrawDirtyRects { get; set; }
/// <summary>
/// Gets or sets a path to which rendered frame should be rendered for debugging.
/// </summary>
public string DebugFramesPath { get; set; }
/// <inheritdoc/>
public void AddDirty(IVisual visual)
{
_dirty?.Add(visual);
}
/// <summary>
/// Disposes of the renderer and detaches from the render loop.
/// </summary>
public void Dispose()
{
if (_renderLoop != null)
@ -90,6 +123,7 @@ namespace Avalonia.Rendering
}
}
/// <inheritdoc/>
public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter)
{
if (_renderLoop == null && (_dirty == null || _dirty.Count > 0))
@ -101,19 +135,23 @@ namespace Avalonia.Rendering
return _scene.HitTest(p, filter);
}
/// <inheritdoc/>
public void Paint(Rect rect)
{
}
/// <inheritdoc/>
public void Resized(Size size)
{
}
/// <inheritdoc/>
Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush)
{
return (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual]?.Size ?? Size.Empty;
}
/// <inheritdoc/>
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
{
var childScene = (_currentDraw as BrushDrawOperation)?.ChildScenes?[brush.Visual];

26
src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs

@ -1,4 +1,7 @@
using System;
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
@ -6,19 +9,40 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Base class for draw operations that can use a brush.
/// </summary>
internal abstract class BrushDrawOperation : IDrawOperation
{
/// <inheritdoc/>
public abstract Rect Bounds { get; }
/// <inheritdoc/>
public abstract bool HitTest(Point p);
/// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes.
/// </summary>
public abstract IDictionary<IVisual, Scene> ChildScenes { get; }
/// <inheritdoc/>
public abstract void Render(IDrawingContextImpl context);
/// <summary>
/// Converts a possibly mutable brush to an immutable brush.
/// </summary>
/// <param name="brush">The brush.</param>
/// <returns>An immutable brush</returns>
protected IBrush ToImmutable(IBrush brush)
{
return (brush as IMutableBrush)?.ToImmutable() ?? brush;
}
/// <summary>
/// Converts pen with a possibly mutable brush to a pen with an immutable brush.
/// </summary>
/// <param name="pen">The pen.</param>
/// <returns>A pen with an immutable brush</returns>
protected Pen ToImmutable(Pen pen)
{
var brush = pen?.Brush != null ? ToImmutable(pen.Brush) : null;

51
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -9,6 +9,9 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A drawing context which builds a scene graph.
/// </summary>
internal class DeferredDrawingContextImpl : IDrawingContextImpl
{
private readonly ISceneBuilder _sceneBuilder;
@ -16,16 +19,34 @@ namespace Avalonia.Rendering.SceneGraph
private int _childIndex;
private int _drawOperationindex;
/// <summary>
/// Initializes a new instance of the <see cref="DeferredDrawingContextImpl"/> class.
/// </summary>
/// <param name="sceneBuilder">
/// A scene builder used for constructing child scenes for visual brushes.
/// </param>
/// <param name="layers">The scene layers.</param>
public DeferredDrawingContextImpl(ISceneBuilder sceneBuilder, SceneLayers layers)
{
_sceneBuilder = sceneBuilder;
Layers = layers;
}
/// <inheritdoc/>
public Matrix Transform { get; set; } = Matrix.Identity;
/// <summary>
/// Gets the layers in the scene being built.
/// </summary>
public SceneLayers Layers { get; }
/// <summary>
/// Informs the drawing context of the visual node that is about to be rendered.
/// </summary>
/// <param name="node">The visual node.</param>
/// <returns>
/// An object which when disposed will commit the changes to visual node.
/// </returns>
public UpdateState BeginUpdate(VisualNode node)
{
Contract.Requires<ArgumentNullException>(node != null);
@ -50,21 +71,33 @@ namespace Avalonia.Rendering.SceneGraph
return state;
}
/// <inheritdoc/>
public void Clear(Color color)
{
// Cannot clear a deferred scene.
}
/// <inheritdoc/>
public void Dispose()
{
// Nothing to do here as we allocate no unmanaged resources.
}
/// <summary>
/// Removes any remaining drawing operations from the visual node.
/// </summary>
/// <remarks>
/// Drawing operations are updated in place, overwriting existing drawing operations if
/// they are different. Once drawing has completed for the current visual node, it is
/// possible that there are stale drawing operations at the end of the list. This method
/// trims these stale drawing operations.
/// </remarks>
public void TrimChildren()
{
_node.TrimChildren(_childIndex);
}
/// <inheritdoc/>
public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
{
var next = NextDrawAs<GeometryNode>();
@ -79,6 +112,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
var next = NextDrawAs<ImageNode>();
@ -93,18 +127,20 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void DrawLine(Pen pen, Point p1, Point p2)
{
var next = NextDrawAs<LineNode>();
if (next == null || !next.Equals(Transform, pen, p1, p2))
{
Add(new LineNode(Transform, pen, p1, p2));
Add(new LineNode(Transform, pen, p1, p2, CreateChildScene(pen.Brush)));
}
else
{
@ -112,6 +148,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0)
{
var next = NextDrawAs<RectangleNode>();
@ -126,13 +163,14 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{
var next = NextDrawAs<TextNode>();
if (next == null || !next.Equals(Transform, foreground, origin, text))
{
Add(new TextNode(Transform, foreground, origin, text));
Add(new TextNode(Transform, foreground, origin, text, CreateChildScene(foreground)));
}
else
{
@ -140,6 +178,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0)
{
var next = NextDrawAs<RectangleNode>();
@ -154,41 +193,49 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public void PopClip()
{
// TODO: Implement
}
/// <inheritdoc/>
public void PopGeometryClip()
{
// TODO: Implement
}
/// <inheritdoc/>
public void PopOpacity()
{
// TODO: Implement
}
/// <inheritdoc/>
public void PopOpacityMask()
{
// TODO: Implement
}
/// <inheritdoc/>
public void PushClip(Rect clip)
{
// TODO: Implement
}
/// <inheritdoc/>
public void PushGeometryClip(IGeometryImpl clip)
{
// TODO: Implement
}
/// <inheritdoc/>
public void PushOpacity(double opacity)
{
// TODO: Implement
}
/// <inheritdoc/>
public void PushOpacityMask(IBrush mask, Rect bounds)
{
// TODO: Implement

44
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@ -9,8 +9,19 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a geometry draw.
/// </summary>
internal class GeometryNode : BrushDrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="GeometryNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public GeometryNode(
Matrix transform,
IBrush brush,
@ -26,13 +37,44 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }
/// <summary>
/// Gets the fill brush.
/// </summary>
public IBrush Brush { get; }
/// <summary>
/// Gets the stroke pen.
/// </summary>
public Pen Pen { get; }
/// <summary>
/// Gets the geometry to draw.
/// </summary>
public IGeometryImpl Geometry { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="brush">The fill of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="geometry">The geometry of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry)
{
return transform == Transform &&
@ -41,12 +83,14 @@ namespace Avalonia.Rendering.SceneGraph
Equals(geometry, Geometry);
}
/// <inheritdoc/>
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
context.DrawGeometry(Brush, Pen, Geometry);
}
/// <inheritdoc/>
public override bool HitTest(Point p)
{
p *= Transform.Invert();

16
src/Avalonia.Visuals/Rendering/SceneGraph/ISceneBuilder.cs

@ -2,9 +2,23 @@
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Builds a scene graph from a visual tree.
/// </summary>
public interface ISceneBuilder
{
bool Update(Scene scene, IVisual visual);
/// <summary>
/// Builds the initial scene graph for a visual tree.
/// </summary>
/// <param name="scene">The scene to build.</param>
void UpdateAll(Scene scene);
/// <summary>
/// Updates the visual (and potentially its children) in a scene.
/// </summary>
/// <param name="scene">The scene.</param>
/// <param name="visual">The visual to update.</param>
/// <returns>True if changes were made, otherwise false.</returns>
bool Update(Scene scene, IVisual visual);
}
}

50
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@ -2,14 +2,23 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents an image draw.
/// </summary>
internal class ImageNode : IDrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="source">The image to draw.</param>
/// <param name="opacity">The draw opacity.</param>
/// <param name="sourceRect">The source rect.</param>
/// <param name="destRect">The destination rect.</param>
public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
Bounds = destRect.TransformToAABB(transform);
@ -20,14 +29,47 @@ namespace Avalonia.Rendering.SceneGraph
DestRect = destRect;
}
/// <inheritdoc/>
public Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }
/// <summary>
/// Gets the image to draw.
/// </summary>
public IBitmapImpl Source { get; }
/// <summary>
/// Gets the draw opacity.
/// </summary>
public double Opacity { get; }
/// <summary>
/// Gets the source rect.
/// </summary>
public Rect SourceRect { get; }
/// <summary>
/// Gets the destination rect.
/// </summary>
public Rect DestRect { get; }
public IDictionary<VisualBrush, Scene> ChildScenes => null;
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="source">The image of the other draw operation.</param>
/// <param name="opacity">The opacity of the other draw operation.</param>
/// <param name="sourceRect">The source rect of the other draw operation.</param>
/// <param name="destRect">The dest rect of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
return transform == Transform &&
@ -37,12 +79,14 @@ namespace Avalonia.Rendering.SceneGraph
destRect == DestRect;
}
/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
context.DrawImage(Source, Opacity, SourceRect, DestRect);
}
/// <inheritdoc/>
public bool HitTest(Point p) => Bounds.Contains(p);
}
}

52
src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs

@ -9,24 +9,72 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a line draw.
/// </summary>
internal class LineNode : BrushDrawOperation
{
public LineNode(Matrix transform, Pen pen, Point p1, Point p2)
/// <summary>
/// Initializes a new instance of the <see cref="GeometryNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="pen">The stroke pen.</param>
/// <param name="p1">The start point of the line.</param>
/// <param name="p2">The end point of the line.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public LineNode(
Matrix transform,
Pen pen,
Point p1,
Point p2,
IDictionary<IVisual, Scene> childScenes = null)
{
Bounds = new Rect(P1, P2);
Transform = transform;
Pen = ToImmutable(pen);
P1 = p1;
P2 = p2;
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }
/// <summary>
/// Gets the stroke pen.
/// </summary>
public Pen Pen { get; }
/// <summary>
/// Gets the start point of the line.
/// </summary>
public Point P1 { get; }
/// <summary>
/// Gets the end point of the line.
/// </summary>
public Point P2 { get; }
public override IDictionary<IVisual, Scene> ChildScenes => null;
/// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="p1">The start point of the other draw operation.</param>
/// <param name="p2">The end point of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(Matrix transform, Pen pen, Point p1, Point p2)
{
return transform == Transform && pen == Pen && p1 == P1 && p2 == P2;

50
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@ -9,8 +9,20 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a rectangle draw.
/// </summary>
internal class RectangleNode : BrushDrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="RectangleNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param>
/// <param name="rect">The rectanle to draw.</param>
/// <param name="cornerRadius">The rectangle corner radius.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public RectangleNode(
Matrix transform,
IBrush brush,
@ -28,14 +40,50 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }
/// <summary>
/// Gets the fill brush.
/// </summary>
public IBrush Brush { get; }
/// <summary>
/// Gets the stroke pen.
/// </summary>
public Pen Pen { get; }
/// <summary>
/// Gets the rectangle to draw.
/// </summary>
public Rect Rect { get; }
/// <summary>
/// Gets the rectangle corner radius.
/// </summary>
public float CornerRadius { get; }
/// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; }
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="brush">The fill of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="rect">The rectangle of the other draw operation.</param>
/// <param name="cornerRadius">The rectangle corner radius of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
public bool Equals(Matrix transform, IBrush brush, Pen pen, Rect rect, float cornerRadius)
{
return transform == Transform &&
@ -45,6 +93,7 @@ namespace Avalonia.Rendering.SceneGraph
cornerRadius == CornerRadius;
}
/// <inheritdoc/>
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
@ -60,6 +109,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public override bool HitTest(Point p) => Bounds.Contains(p);
}
}

54
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -3,15 +3,21 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Represents a scene graph used by the <see cref="DeferredRenderer"/>.
/// </summary>
public class Scene
{
private Dictionary<IVisual, IVisualNode> _index;
/// <summary>
/// Initializes a new instance of the <see cref="Scene"/> class.
/// </summary>
/// <param name="rootVisual">The root visual to draw.</param>
public Scene(IVisual rootVisual)
: this(
new VisualNode(rootVisual, null),
@ -22,7 +28,7 @@ namespace Avalonia.Rendering.SceneGraph
_index.Add(rootVisual, Root);
}
internal Scene(VisualNode root, Dictionary<IVisual, IVisualNode> index, SceneLayers layers, int id)
private Scene(VisualNode root, Dictionary<IVisual, IVisualNode> index, SceneLayers layers, int id)
{
Contract.Requires<ArgumentNullException>(root != null);
@ -35,12 +41,35 @@ namespace Avalonia.Rendering.SceneGraph
root.LayerRoot = root.Visual;
}
/// <summary>
/// Gets an ID identifying the scene. This is incremented each time the scene is cloned.
/// </summary>
public int Id { get; }
/// <summary>
/// Gets the layers for the scene.
/// </summary>
public SceneLayers Layers { get; }
/// <summary>
/// Gets the root node of the scene graph.
/// </summary>
public IVisualNode Root { get; }
/// <summary>
/// Gets or sets the size of the scene in device independent pixels.
/// </summary>
public Size Size { get; set; }
/// <summary>
/// Gets or sets the scene scaling.
/// </summary>
public double Scaling { get; set; } = 1;
/// <summary>
/// Adds a node to the scene index.
/// </summary>
/// <param name="node">The node.</param>
public void Add(IVisualNode node)
{
Contract.Requires<ArgumentNullException>(node != null);
@ -48,6 +77,10 @@ namespace Avalonia.Rendering.SceneGraph
_index.Add(node.Visual, node);
}
/// <summary>
/// Clones the scene.
/// </summary>
/// <returns>The cloned scene.</returns>
public Scene Clone()
{
var index = new Dictionary<IVisual, IVisualNode>();
@ -62,6 +95,13 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
/// <summary>
/// Tries to find a node in the scene graph representing the specified visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>
/// The node representing the visual or null if it could not be found.
/// </returns>
public IVisualNode FindNode(IVisual visual)
{
IVisualNode node;
@ -69,11 +109,21 @@ namespace Avalonia.Rendering.SceneGraph
return node;
}
/// <summary>
/// Gets the visuals at a point in the scene.
/// </summary>
/// <param name="p">The point.</param>
/// <param name="filter">A filter. May be null.</param>
/// <returns>The visuals at the specified point.</returns>
public IEnumerable<IVisual> HitTest(Point p, Func<IVisual, bool> filter)
{
return HitTest(Root, p, null, filter);
}
/// <summary>
/// Removes a node from the scene index.
/// </summary>
/// <param name="node">The node.</param>
public void Remove(IVisualNode node)
{
Contract.Requires<ArgumentNullException>(node != null);

5
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -10,8 +10,12 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Builds a scene graph from a visual tree.
/// </summary>
public class SceneBuilder : ISceneBuilder
{
/// <inheritdoc/>
public void UpdateAll(Scene scene)
{
Contract.Requires<ArgumentNullException>(scene != null);
@ -27,6 +31,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <inheritdoc/>
public bool Update(Scene scene, IVisual visual)
{
Contract.Requires<ArgumentNullException>(scene != null);

39
src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs

@ -5,8 +5,16 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Represents a layer in a <see cref="Scene"/>.
/// </summary>
public class SceneLayer
{
/// <summary>
/// Initializes a new instance of the <see cref="SceneLayer"/> class.
/// </summary>
/// <param name="layerRoot">The visual at the root of the layer.</param>
/// <param name="distanceFromRoot">The distance from the scene root.</param>
public SceneLayer(IVisual layerRoot, int distanceFromRoot)
{
LayerRoot = layerRoot;
@ -14,6 +22,10 @@ namespace Avalonia.Rendering.SceneGraph
DistanceFromRoot = distanceFromRoot;
}
/// <summary>
/// Clones the layer.
/// </summary>
/// <returns>The cloned layer.</returns>
public SceneLayer Clone()
{
return new SceneLayer(LayerRoot, DistanceFromRoot)
@ -25,12 +37,39 @@ namespace Avalonia.Rendering.SceneGraph
};
}
/// <summary>
/// Gets the visual at the root of the layer.
/// </summary>
public IVisual LayerRoot { get; }
/// <summary>
/// Gets the dirty rectangles for the layer.
/// </summary>
public DirtyRects Dirty { get; }
/// <summary>
/// Gets the distance of the layer root from the root of the scene.
/// </summary>
public int DistanceFromRoot { get; }
/// <summary>
/// Gets or sets the opacity of the layer.
/// </summary>
public double Opacity { get; set; } = 1;
/// <summary>
/// Gets or sets the opacity mask for the layer.
/// </summary>
public IBrush OpacityMask { get; set; }
/// <summary>
/// Gets or sets the target rectangle for the layer opacity mask.
/// </summary>
public Rect OpacityMaskRect { get; set; }
/// <summary>
/// Gets the layer's geometry clip.
/// </summary>
public IGeometryImpl GeometryClip { get; set; }
}
}

63
src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs

@ -5,19 +5,32 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Holds a collection of layers for a <see cref="Scene"/>.
/// </summary>
public class SceneLayers : IEnumerable<SceneLayer>
{
private readonly IVisual _root;
private readonly List<SceneLayer> _inner = new List<SceneLayer>();
private readonly Dictionary<IVisual, SceneLayer> _index = new Dictionary<IVisual, SceneLayer>();
/// <summary>
/// Initializes a new instance of the <see cref="SceneLayers"/> class.
/// </summary>
/// <param name="root">The scene's root visual.</param>
public SceneLayers(IVisual root)
{
_root = root;
}
/// <summary>
/// Gets the number of layers in the scene.
/// </summary>
public int Count => _inner.Count;
/// <summary>
/// Gets a value indicating whether any of the layers have a dirty region.
/// </summary>
public bool HasDirty
{
get
@ -34,9 +47,25 @@ namespace Avalonia.Rendering.SceneGraph
}
}
/// <summary>
/// Gets a layer by index.
/// </summary>
/// <param name="index">The index of the layer.</param>
/// <returns>The layer.</returns>
public SceneLayer this[int index] => _inner[index];
/// <summary>
/// Gets a layer by its root visual.
/// </summary>
/// <param name="visual">The layer's root visual.</param>
/// <returns>The layer.</returns>
public SceneLayer this[IVisual visual] => _index[visual];
/// <summary>
/// Adds a layer to the scene.
/// </summary>
/// <param name="layerRoot">The root visual of the layer.</param>
/// <returns>The created layer.</returns>
public SceneLayer Add(IVisual layerRoot)
{
Contract.Requires<ArgumentNullException>(layerRoot != null);
@ -49,6 +78,10 @@ namespace Avalonia.Rendering.SceneGraph
return layer;
}
/// <summary>
/// Makes a deep clone of the layers.
/// </summary>
/// <returns>The cloned layers.</returns>
public SceneLayers Clone()
{
var result = new SceneLayers(_root);
@ -63,6 +96,13 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
/// <summary>
/// Tests whether a layer exists with the specified root visual.
/// </summary>
/// <param name="layerRoot">The root visual.</param>
/// <returns>
/// True if a layer exists with the specified root visual, otherwise false.
/// </returns>
public bool Exists(IVisual layerRoot)
{
Contract.Requires<ArgumentNullException>(layerRoot != null);
@ -70,6 +110,11 @@ namespace Avalonia.Rendering.SceneGraph
return _index.ContainsKey(layerRoot);
}
/// <summary>
/// Tries to find a layer with the specified root visual.
/// </summary>
/// <param name="layerRoot">The root visual.</param>
/// <returns>The layer if found, otherwise null.</returns>
public SceneLayer Find(IVisual layerRoot)
{
SceneLayer result;
@ -77,6 +122,11 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
/// <summary>
/// Gets an existing layer or creates a new one if no existing layer is found.
/// </summary>
/// <param name="layerRoot">The root visual.</param>
/// <returns>The layer.</returns>
public SceneLayer GetOrAdd(IVisual layerRoot)
{
Contract.Requires<ArgumentNullException>(layerRoot != null);
@ -91,6 +141,11 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
/// <summary>
/// Removes a layer from the scene.
/// </summary>
/// <param name="layerRoot">The root visual.</param>
/// <returns>True if a matching layer was removed, otherwise false.</returns>
public bool Remove(IVisual layerRoot)
{
Contract.Requires<ArgumentNullException>(layerRoot != null);
@ -105,6 +160,11 @@ namespace Avalonia.Rendering.SceneGraph
return layer != null;
}
/// <summary>
/// Removes a layer from the scene.
/// </summary>
/// <param name="layer">The layer.</param>
/// <returns>True if the layer was part of the scene, otherwise false.</returns>
public bool Remove(SceneLayer layer)
{
Contract.Requires<ArgumentNullException>(layer != null);
@ -113,7 +173,10 @@ namespace Avalonia.Rendering.SceneGraph
return _inner.Remove(layer);
}
/// <inheritdoc/>
public IEnumerator<SceneLayer> GetEnumerator() => _inner.GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private int FindInsertIndex(SceneLayer insert)

54
src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs

@ -9,30 +9,79 @@ using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// A node in the scene graph which represents a text draw.
/// </summary>
internal class TextNode : BrushDrawOperation
{
public TextNode(Matrix transform, IBrush foreground, Point origin, IFormattedTextImpl text)
/// <summary>
/// Initializes a new instance of the <see cref="TextNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="foreground">The foreground brush.</param>
/// <param name="origin">The draw origin.</param>
/// <param name="text">The text to draw.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public TextNode(
Matrix transform,
IBrush foreground,
Point origin,
IFormattedTextImpl text,
IDictionary<IVisual, Scene> childScenes = null)
{
Bounds = new Rect(origin, text.Size).TransformToAABB(transform);
Transform = transform;
Foreground = ToImmutable(foreground);
Origin = origin;
Text = text;
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
public Matrix Transform { get; }
/// <summary>
/// Gets the foreground brush.
/// </summary>
public IBrush Foreground { get; }
/// <summary>
/// Gets the draw origin.
/// </summary>
public Point Origin { get; }
/// <summary>
/// Gets the text to draw.
/// </summary>
public IFormattedTextImpl Text { get; }
public override IDictionary<IVisual, Scene> ChildScenes => null;
/// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; }
/// <inheritdoc/>
public override void Render(IDrawingContextImpl context)
{
context.Transform = Transform;
context.DrawText(Foreground, Origin, Text);
}
/// <summary>
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="foreground">The foregroundof the other draw operation.</param>
/// <param name="origin">The draw origin of the other draw operation.</param>
/// <param name="text">The text of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
/// </remarks>
internal bool Equals(Matrix transform, IBrush foreground, Point origin, IFormattedTextImpl text)
{
return transform == Transform &&
@ -41,6 +90,7 @@ namespace Avalonia.Rendering.SceneGraph
Equals(text, Text);
}
/// <inheritdoc/>
public override bool HitTest(Point p) => Bounds.Contains(p);
}
}

Loading…
Cancel
Save