// 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 System.Linq; using System.Reactive.Disposables; using Avalonia.Media; using Avalonia.Platform; using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph { public class DeferredDrawingContextImpl : IDrawingContextImpl { private Stack _stack = new Stack(); public Matrix Transform { get; set; } private VisualNode Node => _stack.Peek().Node; private int Index { get { return _stack.Peek().Index; } set { _stack.Peek().Index = value; } } public IDisposable Begin(VisualNode node) { if (_stack.Count > 0) { var next = NextNodeAs(); if (next == null || next != node) { Add(node); } else { ++Index; } } _stack.Push(new Frame(node)); return Disposable.Create(Pop); } public void Dispose() { } public void TrimNodes() { var frame = _stack.Peek(); var children = frame.Node.Children; var index = frame.Index; if (children.Count > index) { children.RemoveRange(index, children.Count - index); } } public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry) { var next = NextNodeAs(); if (next == null || !next.Equals(Transform, brush, pen, geometry)) { Add(new GeometryNode(Transform, brush, pen, geometry)); } else { ++Index; } } public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect) { var next = NextNodeAs(); if (next == null || !next.Equals(Transform, source, opacity, sourceRect, destRect)) { Add(new ImageNode(Transform, source, opacity, sourceRect, destRect)); } else { ++Index; } } public void DrawLine(Pen pen, Point p1, Point p2) { var next = NextNodeAs(); if (next == null || !next.Equals(Transform, pen, p1, p2)) { Add(new LineNode(Transform, pen, p1, p2)); } else { ++Index; } } public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0) { var next = NextNodeAs(); if (next == null || !next.Equals(Transform, null, pen, rect, cornerRadius)) { Add(new RectangleNode(Transform, null, pen, rect, cornerRadius)); } else { ++Index; } } public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text) { var next = NextNodeAs(); if (next == null || !next.Equals(Transform, foreground, origin, text)) { Add(new TextNode(Transform, foreground, origin, text)); } else { ++Index; } } public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0) { var next = NextNodeAs(); if (next == null || !next.Equals(Transform, brush, null, rect, cornerRadius)) { Add(new RectangleNode(Transform, brush, null, rect, cornerRadius)); } else { ++Index; } } public void PopClip() { // TODO: Implement } public void PopGeometryClip() { // TODO: Implement } public void PopOpacity() { // TODO: Implement } public void PopOpacityMask() { // TODO: Implement } public void PushClip(Rect clip) { // TODO: Implement } public void PushGeometryClip(Geometry clip) { // TODO: Implement } public void PushOpacity(double opacity) { // TODO: Implement } public void PushOpacityMask(IBrush mask, Rect bounds) { // TODO: Implement } private void Add(ISceneNode node) { var index = Index; if (index < Node.Children.Count) { Node.Children[index] = node; } else { Node.Children.Add(node); } ++Index; } private T NextNodeAs() where T : class, ISceneNode { return Index < Node.Children.Count ? Node.Children[Index] as T : null; } private void Pop() => _stack.Pop(); class Frame { public Frame(VisualNode node) { Node = node; } public VisualNode Node { get; } public int Index { get; set; } } } }