diff --git a/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs b/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs index fd1f457a95..94fb19ea0b 100644 --- a/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs +++ b/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs @@ -101,7 +101,7 @@ namespace Perspex.Cairo.Media var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); using (var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height)) - using (var context = new Context(intermediate)) + using (var ctx = new RenderTarget(intermediate).CreateDrawingContext()) { Rect drawRect; var transform = CalculateIntermediateTransform( @@ -111,12 +111,12 @@ namespace Perspex.Cairo.Media scale, translate, out drawRect); - var renderer = new RenderTarget(intermediate); - context.Rectangle(drawRect.ToCairo()); - context.Clip(); - context.Transform(transform.ToCairo()); - renderer.Render(visual, transform, drawRect); + using (ctx.PushClip(drawRect)) + using (ctx.PushTransform(transform)) + { + ctx.Render(visual); + } var result = new SurfacePattern(intermediate); diff --git a/src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs b/src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs new file mode 100644 index 0000000000..86ae342894 --- /dev/null +++ b/src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs @@ -0,0 +1,76 @@ +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 7824b398e6..e09bed7d90 100644 --- a/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/src/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -99,6 +99,7 @@ + diff --git a/src/Perspex.SceneGraph/Rendering/RendererBase.cs b/src/Perspex.SceneGraph/Rendering/RendererBase.cs index c9f79b2669..e43f458cf9 100644 --- a/src/Perspex.SceneGraph/Rendering/RendererBase.cs +++ b/src/Perspex.SceneGraph/Rendering/RendererBase.cs @@ -27,31 +27,7 @@ namespace Perspex.Rendering ctx.Render(visual); } - /// - /// Renders the specified visual. - /// - /// IRenderer instance - /// The visual to render. - /// The current translation. - /// The current transform. - public static void Render(this IRenderTarget renderTarget, IVisual visual, Matrix translation, Matrix transform) - { - using (var ctx = renderTarget.CreateDrawingContext()) - ctx.Render(visual, translation, transform); - } - /// - /// Renders the specified visual with the specified transform and clip. - /// - /// IRenderer instance - /// The visual to render. - /// The transform. - /// An optional clip rectangle. - public static void Render(this IRenderTarget renderTarget, IVisual visual, Matrix transform, Rect? clip = null) - { - using (var context = renderTarget.CreateDrawingContext()) - context.Render(visual, transform, clip); - } /// /// Renders the specified visual. @@ -60,65 +36,29 @@ namespace Perspex.Rendering /// /// The drawing context. public static void Render(this IDrawingContext context, IVisual visual) - { - context.Render(visual, Matrix.Identity); - } - - /// - /// Renders the specified visual with the specified transform and clip. - /// - /// The visual to render. - /// The drawing context. - /// The transform. - /// An optional clip rectangle. - public static void Render(this IDrawingContext context, IVisual visual, Matrix transform, Rect? clip = null) - { - using (clip.HasValue ? context.PushClip(clip.Value) : null) - { - context.Render(visual, Matrix.Identity, transform); - } - } - - /// - /// Renders the specified visual. - /// - /// The visual to render. - /// The drawing context. - /// The current translation. - /// The current transform. - public static void Render(this IDrawingContext context, IVisual visual, Matrix translation, Matrix transform) { var opacity = visual.Opacity; - if (visual.IsVisible && opacity > 0) { - // Translate any existing transform into this controls coordinate system. - Matrix offset = Matrix.CreateTranslation(visual.Bounds.Position); - transform = offset * transform * -offset; + var m = Matrix.CreateTranslation(visual.Bounds.Position); - // Update the current offset. - translation *= Matrix.CreateTranslation(visual.Bounds.Position); + var renderTransform = Matrix.Identity; - // Apply the control's render transform, if any. if (visual.RenderTransform != null) { - offset = Matrix.CreateTranslation(visual.TransformOrigin.ToPixels(visual.Bounds.Size)); - transform *= -offset * visual.RenderTransform.Value * offset; + var offset = -Matrix.CreateTranslation(visual.TransformOrigin.ToPixels(visual.Bounds.Size)); + renderTransform = offset*visual.RenderTransform.Value; } + m = context.CurrentTransform.Invert()*renderTransform*m*context.CurrentTransform; - // Draw the control and its children. - var m = transform * translation; - var d = context.PushTransform(m); - + using (context.PushTransform(m)) using (context.PushOpacity(opacity)) using (visual.ClipToBounds ? context.PushClip(new Rect(visual.Bounds.Size)) : null) { visual.Render(context); - d.Dispose(); - foreach (var child in visual.VisualChildren.OrderBy(x => x.ZIndex)) { - context.Render(child, translation, transform); + context.Render(child); } } } diff --git a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs index c57c2b1477..ddb82fba19 100644 --- a/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs +++ b/src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs @@ -53,8 +53,12 @@ namespace Perspex.Direct2D1.Media out drawRect); var renderer = new RenderTarget(intermediate); - renderer.Render(visual, transform, drawRect); - + using (var ctx = renderer.CreateDrawingContext()) + using (ctx.PushClip(drawRect)) + using (ctx.PushTransform(transform)) + { + ctx.Render(visual); + } this.PlatformBrush = new BitmapBrush( target, intermediate.Bitmap, diff --git a/src/Windows/Perspex.Direct2D1/RenderTarget.cs b/src/Windows/Perspex.Direct2D1/RenderTarget.cs index ea6400be80..5feabdde87 100644 --- a/src/Windows/Perspex.Direct2D1/RenderTarget.cs +++ b/src/Windows/Perspex.Direct2D1/RenderTarget.cs @@ -91,13 +91,23 @@ 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 new DrawingContext(_renderTarget, DirectWriteFactory); + return Wrap(new DrawingContext(_renderTarget, DirectWriteFactory)); } public void Dispose()