Browse Source

Make SceneBuilder update dirty rects.

scenegraph-after-breakage
Steven Kirk 10 years ago
parent
commit
3c189cbe82
  1. 1
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  2. 36
      src/Avalonia.Visuals/Rendering/DirtyRects.cs
  3. 29
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  4. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  5. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/IGeometryNode.cs
  6. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  7. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
  8. 8
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  9. 38
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  10. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
  11. 10
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -110,6 +110,7 @@
<Compile Include="Rendering\IRenderer.cs" />
<Compile Include="Rendering\IRendererFactory.cs" />
<Compile Include="Rendering\IRenderLoop.cs" />
<Compile Include="Rendering\DirtyRects.cs" />
<Compile Include="Rendering\Renderer.cs" />
<Compile Include="Rendering\RendererMixin.cs" />
<Compile Include="Rendering\DefaultRenderLoop.cs" />

36
src/Avalonia.Visuals/Rendering/DirtyRects.cs

@ -0,0 +1,36 @@
// 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;
using System.Collections.Generic;
namespace Avalonia.Rendering
{
public class DirtyRects
{
private List<Rect> _rects = new List<Rect>();
public void Add(Rect rect)
{
for (var i = 0; i < _rects.Count; ++i)
{
var intersection = _rects[i].Intersect(rect);
if (intersection != Rect.Empty)
{
_rects[i] = intersection;
return;
}
}
_rects.Add(rect);
}
public IList<Rect> Coalesce()
{
// TODO: Final coalesce
return _rects;
}
}
}

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

@ -3,11 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
@ -15,10 +13,22 @@ namespace Avalonia.Rendering.SceneGraph
{
private Stack<Frame> _stack = new Stack<Frame>();
public DeferredDrawingContextImpl()
: this(new DirtyRects())
{
}
public DeferredDrawingContextImpl(DirtyRects dirty)
{
Dirty = dirty;
}
public Matrix Transform { get; set; }
private VisualNode Node => _stack.Peek().Node;
public DirtyRects Dirty { get; }
private int Index
{
get { return _stack.Peek().Index; }
@ -206,7 +216,20 @@ namespace Avalonia.Rendering.SceneGraph
return Index < Node.Children.Count ? Node.Children[Index] as T : null;
}
private void Pop() => _stack.Pop();
private void Pop()
{
foreach (var child in Node.Children)
{
var geometry = child as IGeometryNode;
if (geometry != null)
{
Dirty.Add(geometry.Bounds);
}
}
_stack.Pop();
}
class Frame
{

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

@ -11,12 +11,14 @@ namespace Avalonia.Rendering.SceneGraph
{
public GeometryNode(Matrix transform, IBrush brush, Pen pen, IGeometryImpl geometry)
{
Bounds = geometry.Bounds * transform;
Transform = transform;
Brush = brush;
Pen = pen;
Geometry = geometry;
}
public Rect Bounds { get; }
public Matrix Transform { get; }
public IBrush Brush { get; }
public Pen Pen { get; }

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

@ -10,6 +10,11 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public interface IGeometryNode : ISceneNode
{
/// <summary>
/// Gets the bounds of the node in global coordinates.
/// </summary>
Rect Bounds { get; }
/// <summary>
/// Hit test the geometry in this node.
/// </summary>

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

@ -11,6 +11,7 @@ namespace Avalonia.Rendering.SceneGraph
{
public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
Bounds = destRect * transform;
Transform = transform;
Source = source;
Opacity = opacity;
@ -18,6 +19,7 @@ namespace Avalonia.Rendering.SceneGraph
DestRect = destRect;
}
public Rect Bounds { get; }
public Matrix Transform { get; }
public IBitmapImpl Source { get; }
public double Opacity { get; }
@ -39,9 +41,6 @@ namespace Avalonia.Rendering.SceneGraph
context.DrawImage(Source, Opacity, SourceRect, DestRect);
}
public bool HitTest(Point p)
{
return (DestRect * Transform).Contains(p);
}
public bool HitTest(Point p) => Bounds.Contains(p);
}
}

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

@ -10,12 +10,14 @@ namespace Avalonia.Rendering.SceneGraph
{
public LineNode(Matrix transform, Pen pen, Point p1, Point p2)
{
Bounds = new Rect(P1, P2);
Transform = transform;
Pen = pen;
P1 = p1;
P2 = p2;
}
public Rect Bounds { get; }
public Matrix Transform { get; }
public Pen Pen { get; }
public Point P1 { get; }

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

@ -10,6 +10,7 @@ namespace Avalonia.Rendering.SceneGraph
{
public RectangleNode(Matrix transform, IBrush brush, Pen pen, Rect rect, float cornerRadius)
{
Bounds = rect * transform;
Transform = transform;
Brush = brush;
Pen = pen;
@ -17,6 +18,7 @@ namespace Avalonia.Rendering.SceneGraph
CornerRadius = cornerRadius;
}
public Rect Bounds { get; }
public Matrix Transform { get; }
public IBrush Brush { get; }
public Pen Pen { get; }
@ -47,10 +49,6 @@ namespace Avalonia.Rendering.SceneGraph
}
}
public bool HitTest(Point p)
{
// TODO: Only test interior when Brush != null.
return (Rect * Transform).Contains(p);
}
public bool HitTest(Point p) => Bounds.Contains(p);
}
}

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

@ -6,6 +6,7 @@ using System.Linq;
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
using System.Collections.Generic;
namespace Avalonia.Rendering.SceneGraph
{
@ -13,6 +14,7 @@ namespace Avalonia.Rendering.SceneGraph
{
public static void UpdateAll(Scene scene)
{
Contract.Requires<ArgumentNullException>(scene != null);
Dispatcher.UIThread.VerifyAccess();
using (var impl = new DeferredDrawingContextImpl())
@ -24,6 +26,15 @@ namespace Avalonia.Rendering.SceneGraph
public static bool Update(Scene scene, IVisual visual)
{
var dirty = new DirtyRects();
return Update(scene, visual, dirty);
}
public static bool Update(Scene scene, IVisual visual, DirtyRects dirty)
{
Contract.Requires<ArgumentNullException>(scene != null);
Contract.Requires<ArgumentNullException>(visual != null);
Contract.Requires<ArgumentNullException>(dirty != null);
Dispatcher.UIThread.VerifyAccess();
var node = (VisualNode)scene.FindNode(visual);
@ -44,7 +55,7 @@ namespace Avalonia.Rendering.SceneGraph
// descendents too.
var recurse = node.Visual != visual;
using (var impl = new DeferredDrawingContextImpl())
using (var impl = new DeferredDrawingContextImpl(dirty))
using (var context = new DrawingContext(impl))
{
if (node.Parent != null)
@ -65,7 +76,7 @@ namespace Avalonia.Rendering.SceneGraph
// The control has been removed so remove it from its parent and deindex the
// node and its descendents.
((VisualNode)node.Parent)?.Children.Remove(node);
Deindex(scene, node);
Deindex(scene, node, dirty);
return true;
}
}
@ -76,7 +87,7 @@ namespace Avalonia.Rendering.SceneGraph
// node and its descendents.
var trim = FindFirstDeadAncestor(scene, node);
((VisualNode)trim.Parent).Children.Remove(trim);
Deindex(scene, trim);
Deindex(scene, trim, dirty);
return true;
}
@ -170,18 +181,31 @@ namespace Avalonia.Rendering.SceneGraph
return node;
}
private static void Deindex(Scene scene, VisualNode node)
private static IList<Rect> Deindex(Scene scene, VisualNode node)
{
var dirty = new DirtyRects();
Deindex(scene, node, dirty);
return dirty.Coalesce();
}
private static void Deindex(Scene scene, VisualNode node, DirtyRects dirty)
{
scene.Remove(node);
node.SubTreeUpdated = true;
foreach (var child in node.Children)
{
var visualChild = child as VisualNode;
var geometry = child as IGeometryNode;
var visual = child as VisualNode;
if (geometry != null)
{
dirty.Add(geometry.Bounds);
}
if (visualChild != null)
if (visual != null)
{
Deindex(scene, visualChild);
Deindex(scene, visual, dirty);
}
}
}

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

@ -11,12 +11,14 @@ namespace Avalonia.Rendering.SceneGraph
{
public TextNode(Matrix transform, IBrush foreground, Point origin, IFormattedTextImpl text)
{
Bounds = new Rect(origin, text.Measure()) * transform;
Transform = transform;
Foreground = foreground;
Origin = origin;
Text = text;
}
public Rect Bounds { get; }
public Matrix Transform { get; }
public IBrush Foreground { get; }
public Point Origin { get; }
@ -36,9 +38,6 @@ namespace Avalonia.Rendering.SceneGraph
Equals(text, Text);
}
public bool HitTest(Point p)
{
return (new Rect(Origin, Text.Measure()) * Transform).Contains(p);
}
public bool HitTest(Point p) => Bounds.Contains(p);
}
}

10
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -72,7 +72,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Child = new Border
{
Margin = new Thickness(10, 20, 30, 40),
Child = canvas = new Canvas(),
Child = canvas = new Canvas
{
Background = Brushes.AliceBlue,
}
}
};
@ -282,7 +285,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Background = Brushes.Red,
Child = decorator = new Decorator
{
Child = canvas = new Canvas()
Child = canvas = new Canvas
{
Background = Brushes.AliceBlue,
}
}
}
};

Loading…
Cancel
Save