From 3fc62506df5ea7d5918183614af8417b70924fc7 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 3 Oct 2015 10:31:14 +0300 Subject: [PATCH] Introduced DrawingContext class responsible for matrix transformation and push/pop order validation --- src/Gtk/Perspex.Cairo/Media/DrawingContext.cs | 74 ++++---- .../Media/Imaging/RenderTargetBitmapImpl.cs | 2 +- src/Gtk/Perspex.Cairo/RenderTarget.cs | 12 +- src/Perspex.Application/Application.cs | 2 +- src/Perspex.Controls/Border.cs | 2 +- src/Perspex.Controls/Image.cs | 2 +- src/Perspex.Controls/Panel.cs | 4 +- .../Presenters/TextPresenter.cs | 2 +- src/Perspex.Controls/Primitives/AccessText.cs | 2 +- src/Perspex.Controls/Shapes/Shape.cs | 2 +- src/Perspex.Controls/TextBlock.cs | 2 +- .../Adapters/GraphicsAdapter.cs | 4 +- src/Perspex.HtmlRenderer/HtmlContainer.cs | 2 +- src/Perspex.HtmlRenderer/HtmlControl.cs | 2 +- src/Perspex.SceneGraph/IVisual.cs | 4 +- .../Media/DrawingContext.cs | 160 ++++++++++++++++++ .../Media/IDrawingContext.cs | 19 +-- .../Media/Imaging/RenderTargetBitmap.cs | 2 +- .../Media/ValidatingDrawingContext.cs | 76 --------- .../Perspex.SceneGraph.csproj | 2 +- .../Platform/IRenderTarget.cs | 4 +- .../Rendering/RendererBase.cs | 4 +- src/Perspex.SceneGraph/Visual.cs | 4 +- .../Perspex.Direct2D1/Media/DrawingContext.cs | 59 +++---- .../Media/Imaging/RenderTargetBitmapImpl.cs | 2 +- src/Windows/Perspex.Direct2D1/RenderTarget.cs | 19 +-- 26 files changed, 274 insertions(+), 195 deletions(-) create mode 100644 src/Perspex.SceneGraph/Media/DrawingContext.cs delete mode 100644 src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs diff --git a/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs b/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs index f8c74f3f8f..3efb60e5c5 100644 --- a/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs +++ b/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs @@ -2,6 +2,7 @@ // 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 System.Runtime.InteropServices; @@ -16,7 +17,7 @@ namespace Perspex.Cairo.Media /// /// Draws using Direct2D1. /// - public class DrawingContext : IDrawingContext, IDisposable + public class DrawingContext : IDrawingContextImpl, IDisposable { /// /// The cairo context. @@ -30,7 +31,6 @@ namespace Perspex.Cairo.Media public DrawingContext(Cairo.Surface surface) { _context = new Cairo.Context(surface); - CurrentTransform = Matrix.Identity; } /// @@ -40,15 +40,23 @@ namespace Perspex.Cairo.Media public DrawingContext(Gdk.Drawable drawable) { _context = Gdk.CairoHelper.Create(drawable); - CurrentTransform = Matrix.Identity; } + + private Matrix _transform = Matrix.Identity; /// /// Gets the current transform of the drawing context. /// - public Matrix CurrentTransform + public Matrix Transform { - get; } + get { return _transform; } + set + { + _transform = value; + _context.Matrix = value.ToCairo(); + + } + } /// /// Ends a draw operation. @@ -131,29 +139,31 @@ namespace Perspex.Cairo.Media { var impl = geometry.PlatformImpl as StreamGeometryImpl; - using (var pop = PushTransform(impl.Transform)) + var oldMatrix = Transform; + Transform *= impl.Transform; + + + + if (brush != null) { _context.AppendPath(impl.Path); - - if (brush != null) + using (var b = SetBrush(brush, geometry.Bounds.Size)) { - using (var b = SetBrush(brush, geometry.Bounds.Size)) - { - if (pen != null) - _context.FillPreserve(); - else - _context.Fill(); - } + if (pen != null) + _context.FillPreserve(); + else + _context.Fill(); } } - if (pen != null) { - using (var p = SetPen(pen, geometry.Bounds.Size)) - { - _context.Stroke(); - } + _context.AppendPath(impl.Path); + using (var p = SetPen(pen, geometry.Bounds.Size)) + { + _context.Stroke(); + } } + Transform = oldMatrix; } /// @@ -208,31 +218,37 @@ namespace Perspex.Cairo.Media /// /// The clip rectangle. /// A disposable used to undo the clip rectangle. - public IDisposable PushClip(Rect clip) + public void PushClip(Rect clip) { _context.Save(); _context.Rectangle(clip.ToCairo()); _context.Clip(); + } - return Disposable.Create(() => _context.Restore()); + public void PopClip() + { + _context.Restore(); } + readonly Stack _opacityStack = new Stack(); + /// /// Pushes an opacity value. /// /// The opacity. /// A disposable used to undo the opacity. - public IDisposable PushOpacity(double opacity) + public void PushOpacity(double opacity) { - var tmp = opacityOverride; + _opacityStack.Push(opacityOverride); if (opacity < 1.0f) - opacityOverride = opacity; + opacityOverride *= opacity; - return Disposable.Create(() => - { - opacityOverride = tmp; - }); + } + + public void PopOpacity() + { + opacityOverride = _opacityStack.Pop(); } /// diff --git a/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs b/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs index 0b9183d928..4d8f05848a 100644 --- a/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs +++ b/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs @@ -39,7 +39,7 @@ namespace Perspex.Cairo.Media.Imaging Surface.WriteToPng(fileName); } - public IDrawingContext CreateDrawingContext() + public Perspex.Media.DrawingContext CreateDrawingContext() { return _renderTarget.CreateDrawingContext(); } diff --git a/src/Gtk/Perspex.Cairo/RenderTarget.cs b/src/Gtk/Perspex.Cairo/RenderTarget.cs index 333c558e8a..2daab33fbe 100644 --- a/src/Gtk/Perspex.Cairo/RenderTarget.cs +++ b/src/Gtk/Perspex.Cairo/RenderTarget.cs @@ -7,6 +7,7 @@ using Perspex.Cairo.Media; using Perspex.Media; using Perspex.Platform; using Perspex.Rendering; +using DrawingContext = Perspex.Media.DrawingContext; namespace Perspex.Cairo { @@ -50,12 +51,13 @@ namespace Perspex.Cairo /// /// Creates a cairo surface that targets a platform-specific resource. /// - /// A surface wrapped in an . - public IDrawingContext CreateDrawingContext() + /// A surface wrapped in an . + public DrawingContext CreateDrawingContext() { - if(_surface != null) - return new DrawingContext(_surface); - return new DrawingContext(_window.GdkWindow); + var ctx = _surface != null + ? new Media.DrawingContext(_surface) + : new Media.DrawingContext(_window.GdkWindow); + return new DrawingContext(ctx); } public void Dispose() => _surface?.Dispose(); diff --git a/src/Perspex.Application/Application.cs b/src/Perspex.Application/Application.cs index 88f3a23ffe..1e19736154 100644 --- a/src/Perspex.Application/Application.cs +++ b/src/Perspex.Application/Application.cs @@ -177,7 +177,7 @@ namespace Perspex /// The value of Environment.OSVersion.Platform. protected void InitializeSubsystems(int platformID) { - if (platformID == 4 || platformID == 6) + if (true)//platformID == 4 || platformID == 6) { InitializeSubsystem("Perspex.Cairo"); InitializeSubsystem("Perspex.Gtk"); diff --git a/src/Perspex.Controls/Border.cs b/src/Perspex.Controls/Border.cs index 8a70987cb0..bcb1235f25 100644 --- a/src/Perspex.Controls/Border.cs +++ b/src/Perspex.Controls/Border.cs @@ -83,7 +83,7 @@ namespace Perspex.Controls /// Renders the control. /// /// The drawing context. - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { var background = Background; var borderBrush = BorderBrush; diff --git a/src/Perspex.Controls/Image.cs b/src/Perspex.Controls/Image.cs index 79b79c6141..0b316689bd 100644 --- a/src/Perspex.Controls/Image.cs +++ b/src/Perspex.Controls/Image.cs @@ -46,7 +46,7 @@ namespace Perspex.Controls /// Renders the control. /// /// The drawing context. - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { Bitmap source = Source; diff --git a/src/Perspex.Controls/Panel.cs b/src/Perspex.Controls/Panel.cs index 08e6621adb..bd45bf1e3a 100644 --- a/src/Perspex.Controls/Panel.cs +++ b/src/Perspex.Controls/Panel.cs @@ -172,10 +172,10 @@ namespace Perspex.Controls } /// - /// Renders the visual to a . + /// Renders the visual to a . /// /// The drawing context. - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { Brush background = Background; if (background != null) diff --git a/src/Perspex.Controls/Presenters/TextPresenter.cs b/src/Perspex.Controls/Presenters/TextPresenter.cs index 1e6068fd7d..2948cd541a 100644 --- a/src/Perspex.Controls/Presenters/TextPresenter.cs +++ b/src/Perspex.Controls/Presenters/TextPresenter.cs @@ -69,7 +69,7 @@ namespace Perspex.Controls.Presenters return hit.TextPosition + (hit.IsTrailing ? 1 : 0); } - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; diff --git a/src/Perspex.Controls/Primitives/AccessText.cs b/src/Perspex.Controls/Primitives/AccessText.cs index 33da32037f..0168974248 100644 --- a/src/Perspex.Controls/Primitives/AccessText.cs +++ b/src/Perspex.Controls/Primitives/AccessText.cs @@ -62,7 +62,7 @@ namespace Perspex.Controls.Primitives /// Renders the to a drawing context. /// /// The drawing context. - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { base.Render(context); diff --git a/src/Perspex.Controls/Shapes/Shape.cs b/src/Perspex.Controls/Shapes/Shape.cs index 5f5b989602..58dc24a738 100644 --- a/src/Perspex.Controls/Shapes/Shape.cs +++ b/src/Perspex.Controls/Shapes/Shape.cs @@ -90,7 +90,7 @@ namespace Perspex.Controls.Shapes set { SetValue(StrokeThicknessProperty, value); } } - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { var geometry = RenderedGeometry; diff --git a/src/Perspex.Controls/TextBlock.cs b/src/Perspex.Controls/TextBlock.cs index e621369679..b6bec05240 100644 --- a/src/Perspex.Controls/TextBlock.cs +++ b/src/Perspex.Controls/TextBlock.cs @@ -214,7 +214,7 @@ namespace Perspex.Controls /// Renders the to a drawing context. /// /// The drawing context. - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { Brush background = Background; diff --git a/src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs b/src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs index 8b52a1b573..6fd918a69b 100644 --- a/src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs +++ b/src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs @@ -32,7 +32,7 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters /// /// The wrapped Perspex graphics object /// - private readonly IDrawingContext _g; + private readonly DrawingContext _g; /// /// if to release the graphics object on dispose @@ -51,7 +51,7 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters /// the Perspex graphics object to use /// the initial clip of the graphics /// optional: if to release the graphics object on dispose (default - false) - public GraphicsAdapter(IDrawingContext g, RRect initialClip, bool releaseGraphics = false) + public GraphicsAdapter(DrawingContext g, RRect initialClip, bool releaseGraphics = false) : base(PerspexAdapter.Instance, initialClip) { ArgChecker.AssertArgNotNull(g, "g"); diff --git a/src/Perspex.HtmlRenderer/HtmlContainer.cs b/src/Perspex.HtmlRenderer/HtmlContainer.cs index 9ada42d5ed..e30036b343 100644 --- a/src/Perspex.HtmlRenderer/HtmlContainer.cs +++ b/src/Perspex.HtmlRenderer/HtmlContainer.cs @@ -360,7 +360,7 @@ namespace TheArtOfDev.HtmlRenderer.Perspex /// /// the device to use to render /// the clip rectangle of the html container - public void PerformPaint(IDrawingContext g, Rect clip) + public void PerformPaint(DrawingContext g, Rect clip) { ArgChecker.AssertArgNotNull(g, "g"); diff --git a/src/Perspex.HtmlRenderer/HtmlControl.cs b/src/Perspex.HtmlRenderer/HtmlControl.cs index b720b17bc0..ddd7541923 100644 --- a/src/Perspex.HtmlRenderer/HtmlControl.cs +++ b/src/Perspex.HtmlRenderer/HtmlControl.cs @@ -327,7 +327,7 @@ namespace Perspex.Controls.Html private Size RenderSize => new Size(Bounds.Width, Bounds.Height); - public override void Render(IDrawingContext context) + public override void Render(DrawingContext context) { context.FillRectangle(Background, new Rect(RenderSize)); diff --git a/src/Perspex.SceneGraph/IVisual.cs b/src/Perspex.SceneGraph/IVisual.cs index 7126558483..0491d63fad 100644 --- a/src/Perspex.SceneGraph/IVisual.cs +++ b/src/Perspex.SceneGraph/IVisual.cs @@ -76,10 +76,10 @@ namespace Perspex int ZIndex { get; set; } /// - /// Renders the scene graph node to a . + /// Renders the scene graph node to a . /// /// The context. - void Render(IDrawingContext context); + void Render(DrawingContext context); /// /// Returns a transform that transforms the visual's coordinates into the coordinates diff --git a/src/Perspex.SceneGraph/Media/DrawingContext.cs b/src/Perspex.SceneGraph/Media/DrawingContext.cs new file mode 100644 index 0000000000..5f95ba021b --- /dev/null +++ b/src/Perspex.SceneGraph/Media/DrawingContext.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Perspex.Media.Imaging; + +namespace Perspex.Media +{ + public sealed class DrawingContext : IDisposable + { + private readonly IDrawingContextImpl _impl; + private int _currentLevel; + + public DrawingContext(IDrawingContextImpl impl) + { + _impl = impl; + } + + /// + /// Gets the current transform of the drawing context. + /// + public Matrix CurrentTransform => _impl.Transform; + + /// + /// Draws a bitmap image. + /// + /// The bitmap image. + /// The opacity to draw with. + /// The rect in the image to draw. + /// The rect in the output to draw to. + public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) + => _impl.DrawImage(source, opacity, sourceRect, destRect); + + /// + /// Draws a line. + /// + /// The stroke pen. + /// The first point of the line. + /// The second point of the line. + public void DrawLine(Pen pen, Point p1, Point p2) => _impl.DrawLine(pen, p1, p2); + + /// + /// Draws a geometry. + /// + /// The fill brush. + /// The stroke pen. + /// The geometry. + public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) => _impl.DrawGeometry(brush, pen, geometry); + + /// + /// Draws the outline of a rectangle. + /// + /// The pen. + /// The rectangle bounds. + /// The corner radius. + public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0.0f) + => _impl.DrawRectangle(pen, rect, cornerRadius); + + /// + /// Draws text. + /// + /// The foreground brush. + /// The upper-left corner of the text. + /// The text. + public void DrawText(Brush foreground, Point origin, FormattedText text) + => _impl.DrawText(foreground, origin, text); + + /// + /// Draws a filled rectangle. + /// + /// The brush. + /// The rectangle bounds. + /// The corner radius. + public void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0.0f) + => _impl.FillRectangle(brush, rect, cornerRadius); + + public struct PushedState : IDisposable + { + private readonly int _level; + private readonly DrawingContext _context; + private readonly Matrix _matrix; + private readonly PushedStateType _type; + + public enum PushedStateType + { + None, + Matrix, + Opacity, + Clip + } + + public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix)) + { + _level = context._currentLevel += 1; + _context = context; + _type = type; + _matrix = matrix; + + } + + public void Dispose() + { + if(_type == PushedStateType.None) + return; + if (_context._currentLevel != _level) + throw new InvalidOperationException("Wrong Push/Pop state order"); + _context._currentLevel--; + if (_type == PushedStateType.Matrix) + _context._impl.Transform = _matrix; + else if(_type == PushedStateType.Clip) + _context._impl.PopClip(); + + else if(_type == PushedStateType.Opacity) + _context._impl.PopOpacity(); + } + + + } + + + /// + /// Pushes a clip rectange. + /// + /// The clip rectangle. + /// A disposable used to undo the clip rectangle. + public PushedState PushClip(Rect clip) + { + _impl.PushClip(clip); + return new PushedState(this, PushedState.PushedStateType.Clip); + } + + /// + /// Pushes an opacity value. + /// + /// The opacity. + /// A disposable used to undo the opacity. + public PushedState PushOpacity(double opacity) + //TODO: Elimintate platform-specific push opacity call + { + _impl.PushOpacity(opacity); + return new PushedState(this, PushedState.PushedStateType.Opacity); + } + + /// + /// Pushes a matrix transformation. + /// + /// The matrix + /// A disposable used to undo the transformation. + public PushedState PushTransform(Matrix matrix) + { + var oldMatrix = CurrentTransform; + matrix = oldMatrix*matrix; + _impl.Transform = matrix; + return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix); + } + + public void Dispose() => _impl.Dispose(); + } +} diff --git a/src/Perspex.SceneGraph/Media/IDrawingContext.cs b/src/Perspex.SceneGraph/Media/IDrawingContext.cs index 92d1fc4e98..4d144b33e0 100644 --- a/src/Perspex.SceneGraph/Media/IDrawingContext.cs +++ b/src/Perspex.SceneGraph/Media/IDrawingContext.cs @@ -9,12 +9,12 @@ namespace Perspex.Media /// /// Defines the interface through which drawing occurs. /// - public interface IDrawingContext : IDisposable + public interface IDrawingContextImpl : IDisposable { /// - /// Gets the current transform of the drawing context. + /// Gets or sets the current transform of the drawing context. /// - Matrix CurrentTransform { get; } + Matrix Transform { get; set; } /// /// Draws a bitmap image. @@ -70,20 +70,17 @@ namespace Perspex.Media /// /// The clip rectangle. /// A disposable used to undo the clip rectangle. - IDisposable PushClip(Rect clip); + void PushClip(Rect clip); + + void PopClip(); /// /// Pushes an opacity value. /// /// The opacity. /// A disposable used to undo the opacity. - IDisposable PushOpacity(double opacity); + void PushOpacity(double opacity); - /// - /// Pushes a matrix transformation. - /// - /// The matrix - /// A disposable used to undo the transformation. - IDisposable PushTransform(Matrix matrix); + void PopOpacity(); } } diff --git a/src/Perspex.SceneGraph/Media/Imaging/RenderTargetBitmap.cs b/src/Perspex.SceneGraph/Media/Imaging/RenderTargetBitmap.cs index 5b8bbea6da..20fc55ebd8 100644 --- a/src/Perspex.SceneGraph/Media/Imaging/RenderTargetBitmap.cs +++ b/src/Perspex.SceneGraph/Media/Imaging/RenderTargetBitmap.cs @@ -47,7 +47,7 @@ namespace Perspex.Media.Imaging return factory.CreateRenderTargetBitmap(width, height); } - public IDrawingContext CreateDrawingContext() => PlatformImpl.CreateDrawingContext(); + public DrawingContext CreateDrawingContext() => PlatformImpl.CreateDrawingContext(); void IRenderTarget.Resize(int width, int height) { diff --git a/src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs b/src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs deleted file mode 100644 index 86ae342894..0000000000 --- a/src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Reactive.Disposables; -using Perspex.Media.Imaging; - -namespace Perspex.Media -{ - public class ValidatingDrawingContext : IDrawingContext - { - private readonly IDrawingContext _base; - - public ValidatingDrawingContext(IDrawingContext @base) - { - _base = @base; - } - - public void Dispose() - { - _base.Dispose(); - } - - public Matrix CurrentTransform => _base.CurrentTransform; - public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) - { - _base.DrawImage(source, opacity, sourceRect, destRect); - } - - public void DrawLine(Pen pen, Point p1, Point p2) - { - _base.DrawLine(pen, p1, p2); - } - - public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) - { - _base.DrawGeometry(brush, pen, geometry); - } - - public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0) - { - _base.DrawRectangle(pen, rect, cornerRadius); - } - - public void DrawText(Brush foreground, Point origin, FormattedText text) - { - _base.DrawText(foreground, origin, text); - } - - public void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0) - { - _base.FillRectangle(brush, rect, cornerRadius); - } - - - Stack _stateStack = new Stack(); - - IDisposable Transform(IDisposable disposable) - { - _stateStack.Push(disposable); - return Disposable.Create(() => - { - var current = _stateStack.Peek(); - if (current != disposable) - throw new InvalidOperationException("Invalid push/pop order"); - current.Dispose(); - _stateStack.Pop(); - }); - } - - public IDisposable PushClip(Rect clip) => Transform(_base.PushClip(clip)); - - public IDisposable PushOpacity(double opacity) => Transform(_base.PushOpacity(opacity)); - - public IDisposable PushTransform(Matrix matrix) => Transform(_base.PushTransform(matrix)); - } -} \ No newline at end of file diff --git a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj index e09bed7d90..d2d897142f 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -64,6 +64,7 @@ + @@ -99,7 +100,6 @@ - diff --git a/src/Perspex.SceneGraph/Platform/IRenderTarget.cs b/src/Perspex.SceneGraph/Platform/IRenderTarget.cs index fdbf391a05..a5c8f71d79 100644 --- a/src/Perspex.SceneGraph/Platform/IRenderTarget.cs +++ b/src/Perspex.SceneGraph/Platform/IRenderTarget.cs @@ -15,9 +15,9 @@ namespace Perspex.Platform public interface IRenderTarget : IDisposable { /// - /// Creates an for a rendering session. + /// Creates an for a rendering session. /// - IDrawingContext CreateDrawingContext(); + DrawingContext CreateDrawingContext(); /// /// Resizes the rendered viewport. diff --git a/src/Perspex.SceneGraph/Rendering/RendererBase.cs b/src/Perspex.SceneGraph/Rendering/RendererBase.cs index ba4a0d35c1..679407f7bf 100644 --- a/src/Perspex.SceneGraph/Rendering/RendererBase.cs +++ b/src/Perspex.SceneGraph/Rendering/RendererBase.cs @@ -35,7 +35,7 @@ namespace Perspex.Rendering /// The visual to render. /// /// The drawing context. - public static void Render(this IDrawingContext context, IVisual visual) + public static void Render(this DrawingContext context, IVisual visual) { var opacity = visual.Opacity; if (visual.IsVisible && opacity > 0) @@ -54,7 +54,7 @@ namespace Perspex.Rendering using (context.PushTransform(m)) using (context.PushOpacity(opacity)) - using (visual.ClipToBounds ? context.PushClip(new Rect(visual.Bounds.Size)) : null) + using (visual.ClipToBounds ? context.PushClip(new Rect(visual.Bounds.Size)) : default(DrawingContext.PushedState)) { visual.Render(context); foreach (var child in visual.VisualChildren.OrderBy(x => x.ZIndex)) diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs index e835c6a76c..2bbc290685 100644 --- a/src/Perspex.SceneGraph/Visual.cs +++ b/src/Perspex.SceneGraph/Visual.cs @@ -229,10 +229,10 @@ namespace Perspex } /// - /// Renders the visual to a . + /// Renders the visual to a . /// /// The drawing context. - public virtual void Render(IDrawingContext context) + public virtual void Render(DrawingContext context) { Contract.Requires(context != null); } diff --git a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs index 0b6abe9353..b10208d7e0 100644 --- a/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs @@ -2,6 +2,8 @@ // 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; using System.Reactive.Disposables; using Perspex.Media; using SharpDX; @@ -13,7 +15,7 @@ namespace Perspex.Direct2D1.Media /// /// Draws using Direct2D1. /// - public class DrawingContext : IDrawingContext, IDisposable + public class DrawingContext : IDrawingContextImpl, IDisposable { /// /// The Direct2D1 render target. @@ -42,10 +44,10 @@ namespace Perspex.Direct2D1.Media /// /// Gets the current transform of the drawing context. /// - public Matrix CurrentTransform + public Matrix Transform { get { return _renderTarget.Transform.ToPerspex(); } - private set { _renderTarget.Transform = value.ToDirect2D(); } + set { _renderTarget.Transform = value.ToDirect2D(); } } /// @@ -53,6 +55,8 @@ namespace Perspex.Direct2D1.Media /// public void Dispose() { + foreach (var layer in _layerPool) + layer.Dispose(); _renderTarget.EndDraw(); } @@ -233,22 +237,24 @@ namespace Perspex.Direct2D1.Media /// /// The clip rectangle. /// A disposable used to undo the clip rectangle. - public IDisposable PushClip(Rect clip) + public void PushClip(Rect clip) { _renderTarget.PushAxisAlignedClip(clip.ToSharpDX(), AntialiasMode.PerPrimitive); + } - return Disposable.Create(() => - { - _renderTarget.PopAxisAlignedClip(); - }); + public void PopClip() + { + _renderTarget.PopAxisAlignedClip(); } + Stack _layers = new Stack(); + private readonly Stack _layerPool = new Stack(); /// /// Pushes an opacity value. /// /// The opacity. /// A disposable used to undo the opacity. - public IDisposable PushOpacity(double opacity) + public void PushOpacity(double opacity) { if (opacity < 1) { @@ -256,41 +262,26 @@ namespace Perspex.Direct2D1.Media { ContentBounds = RectangleF.Infinite, MaskTransform = Matrix3x2.Identity, - Opacity = (float)opacity, + Opacity = (float) opacity, }; - var layer = new Layer(_renderTarget); - + var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget); _renderTarget.PushLayer(ref parameters, layer); - return Disposable.Create(() => - { - _renderTarget.PopLayer(); - layer.Dispose(); - }); + _layers.Push(layer); } else - { - return Disposable.Empty; - } + _layers.Push(null); } - /// - /// Pushes a matrix transformation. - /// - /// The matrix - /// A disposable used to undo the transformation. - public IDisposable PushTransform(Matrix matrix) + public void PopOpacity() { - Matrix3x2 m3x2 = matrix.ToDirect2D(); - Matrix3x2 transform = _renderTarget.Transform * m3x2; - _renderTarget.Transform = transform; - - return Disposable.Create(() => + var layer = _layers.Pop(); + if (layer != null) { - m3x2.Invert(); - _renderTarget.Transform = transform * m3x2; - }); + _renderTarget.PopLayer(); + _layerPool.Push(layer); + } } /// diff --git a/src/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs b/src/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs index 97efa00d47..cf785aa444 100644 --- a/src/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs @@ -38,7 +38,7 @@ namespace Perspex.Direct2D1.Media // TODO: } - public IDrawingContext CreateDrawingContext() => new RenderTarget(_target).CreateDrawingContext(); + public Perspex.Media.DrawingContext CreateDrawingContext() => new RenderTarget(_target).CreateDrawingContext(); void IRenderTarget.Resize(int width, int height) { diff --git a/src/Windows/Perspex.Direct2D1/RenderTarget.cs b/src/Windows/Perspex.Direct2D1/RenderTarget.cs index 5feabdde87..66ffe80448 100644 --- a/src/Windows/Perspex.Direct2D1/RenderTarget.cs +++ b/src/Windows/Perspex.Direct2D1/RenderTarget.cs @@ -8,6 +8,7 @@ using Perspex.Platform; using Perspex.Rendering; using SharpDX; using SharpDX.Direct2D1; +using DrawingContext = Perspex.Media.DrawingContext; using DwFactory = SharpDX.DirectWrite.Factory; namespace Perspex.Direct2D1 @@ -91,24 +92,12 @@ namespace Perspex.Direct2D1 window.Resize(new Size2(width, height)); } - IDrawingContext Wrap(IDrawingContext ctx) - { -#if DEBUG - return new ValidatingDrawingContext(ctx); -#endif -#pragma warning disable 162 - return ctx; -#pragma warning restore 162 - } - /// /// Creates a drawing context for a rendering session. /// - /// An . - public IDrawingContext CreateDrawingContext() - { - return Wrap(new DrawingContext(_renderTarget, DirectWriteFactory)); - } + /// An . + public DrawingContext CreateDrawingContext() + => new DrawingContext(new Media.DrawingContext(_renderTarget, DirectWriteFactory)); public void Dispose() {