Browse Source

Merge pull request #912 from AvaloniaUI/port-drawingcontext-changes-from-scenegraph

Ported changes to DrawingContext from scenegraph
Steven Kirk 8 years ago
parent
commit
23e52b15ae

+ 1 - 1
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@@ -107,7 +107,7 @@
     <Compile Include="Media\FormattedTextLine.cs" />
     <Compile Include="Media\FormattedText.cs" />
     <Compile Include="Media\Geometry.cs" />
-    <Compile Include="Media\IDrawingContext.cs" />
+    <Compile Include="Media\IDrawingContextImpl.cs" />
     <Compile Include="Platform\ExportRenderingSubsystemAttribute.cs" />
     <Compile Include="Platform\ILockedFramebuffer.cs" />
     <Compile Include="Platform\IModuleEnvironmentChecker.cs" />

+ 67 - 33
src/Avalonia.Visuals/Media/DrawingContext.cs

@@ -1,18 +1,13 @@
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Avalonia.Media.Imaging;
 
 namespace Avalonia.Media
 {
     public sealed class DrawingContext : IDisposable
     {
-        private readonly IDrawingContextImpl _impl;
         private int _currentLevel;
-        
+
 
         static readonly Stack<Stack<PushedState>> StateStackPool = new Stack<Stack<PushedState>>();
         static readonly Stack<Stack<TransformContainer>> TransformStackPool = new Stack<Stack<TransformContainer>>();
@@ -37,14 +32,15 @@ namespace Avalonia.Media
 
         public DrawingContext(IDrawingContextImpl impl)
         {
-            _impl = impl;
+            PlatformImpl = impl;
         }
 
+        public IDrawingContextImpl PlatformImpl { get; }
 
         private Matrix _currentTransform = Matrix.Identity;
 
         private Matrix _currentContainerTransform = Matrix.Identity;
-        
+
         /// <summary>
         /// Gets the current transform of the drawing context.
         /// </summary>
@@ -54,15 +50,15 @@ namespace Avalonia.Media
             private set
             {
                 _currentTransform = value;
-                var transform = _currentTransform*_currentContainerTransform;
-                _impl.Transform = transform;
+                var transform = _currentTransform * _currentContainerTransform;
+                PlatformImpl.Transform = transform;
             }
         }
 
         //HACK: This is a temporary hack that is used in the render loop 
         //to update TransformedBounds property
         [Obsolete("HACK for render loop, don't use")]
-        internal Matrix CurrentContainerTransform => _currentContainerTransform;        
+        internal Matrix CurrentContainerTransform => _currentContainerTransform;
 
         /// <summary>
         /// Draws a bitmap image.
@@ -72,7 +68,11 @@ namespace Avalonia.Media
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
         public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
-            => _impl.DrawImage(source, opacity, sourceRect, destRect);
+        {
+            Contract.Requires<ArgumentNullException>(source != null);
+
+            PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect);
+        }
 
         /// <summary>
         /// Draws a line.
@@ -80,7 +80,13 @@ namespace Avalonia.Media
         /// <param name="pen">The stroke pen.</param>
         /// <param name="p1">The first point of the line.</param>
         /// <param name="p2">The second point of the line.</param>
-        public void DrawLine(Pen pen, Point p1, Point p2) => _impl.DrawLine(pen, p1, p2);
+        public void DrawLine(Pen pen, Point p1, Point p2)
+        {
+            if (PenIsVisible(pen))
+            {
+                PlatformImpl.DrawLine(pen, p1, p2);
+            }
+        }
 
         /// <summary>
         /// Draws a geometry.
@@ -88,7 +94,13 @@ namespace Avalonia.Media
         /// <param name="brush">The fill brush.</param>
         /// <param name="pen">The stroke pen.</param>
         /// <param name="geometry">The geometry.</param>
-        public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry) => _impl.DrawGeometry(brush, pen, geometry);
+        public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
+        {
+            if (brush != null || PenIsVisible(pen))
+            {
+                PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl);
+            }
+        }
 
         /// <summary>
         /// Draws the outline of a rectangle.
@@ -97,7 +109,12 @@ namespace Avalonia.Media
         /// <param name="rect">The rectangle bounds.</param>
         /// <param name="cornerRadius">The corner radius.</param>
         public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0.0f)
-            => _impl.DrawRectangle(pen, rect, cornerRadius);
+        {
+            if (PenIsVisible(pen))
+            {
+                PlatformImpl.DrawRectangle(pen, rect, cornerRadius);
+            }
+        }
 
         /// <summary>
         /// Draws text.
@@ -106,7 +123,14 @@ namespace Avalonia.Media
         /// <param name="origin">The upper-left corner of the text.</param>
         /// <param name="text">The text.</param>
         public void DrawText(IBrush foreground, Point origin, FormattedText text)
-            => _impl.DrawText(foreground, origin, text);
+        {
+            Contract.Requires<ArgumentNullException>(text != null);
+
+            if (foreground != null)
+            {
+                PlatformImpl.DrawText(foreground, origin, text.PlatformImpl);
+            }
+        }
 
         /// <summary>
         /// Draws a filled rectangle.
@@ -115,7 +139,12 @@ namespace Avalonia.Media
         /// <param name="rect">The rectangle bounds.</param>
         /// <param name="cornerRadius">The corner radius.</param>
         public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f)
-            => _impl.FillRectangle(brush, rect, cornerRadius);
+        {
+            if (brush != null && rect != Rect.Empty)
+            {
+                PlatformImpl.FillRectangle(brush, rect, cornerRadius);
+            }
+        }
 
         public struct PushedState : IDisposable
         {
@@ -146,7 +175,7 @@ namespace Avalonia.Media
 
             public void Dispose()
             {
-                if(_type == PushedStateType.None)
+                if (_type == PushedStateType.None)
                     return;
                 if (_context._currentLevel != _level)
                     throw new InvalidOperationException("Wrong Push/Pop state order");
@@ -155,13 +184,13 @@ namespace Avalonia.Media
                 if (_type == PushedStateType.Matrix)
                     _context.CurrentTransform = _matrix;
                 else if (_type == PushedStateType.Clip)
-                    _context._impl.PopClip();
+                    _context.PlatformImpl.PopClip();
                 else if (_type == PushedStateType.Opacity)
-                    _context._impl.PopOpacity();
+                    _context.PlatformImpl.PopOpacity();
                 else if (_type == PushedStateType.GeometryClip)
-                    _context._impl.PopGeometryClip();
+                    _context.PlatformImpl.PopGeometryClip();
                 else if (_type == PushedStateType.OpacityMask)
-                    _context._impl.PopOpacityMask();
+                    _context.PlatformImpl.PopOpacityMask();
                 else if (_type == PushedStateType.MatrixContainer)
                 {
                     var cont = _context._transformContainers.Pop();
@@ -179,7 +208,7 @@ namespace Avalonia.Media
         /// <returns>A disposable used to undo the clip rectangle.</returns>
         public PushedState PushClip(Rect clip)
         {
-            _impl.PushClip(clip);
+            PlatformImpl.PushClip(clip);
             return new PushedState(this, PushedState.PushedStateType.Clip);
         }
 
@@ -191,7 +220,7 @@ namespace Avalonia.Media
         public PushedState PushGeometryClip(Geometry clip)
         {
             Contract.Requires<ArgumentNullException>(clip != null);
-            _impl.PushGeometryClip(clip);
+            PlatformImpl.PushGeometryClip(clip.PlatformImpl);
             return new PushedState(this, PushedState.PushedStateType.GeometryClip);
         }
 
@@ -201,9 +230,9 @@ namespace Avalonia.Media
         /// <param name="opacity">The opacity.</param>
         /// <returns>A disposable used to undo the opacity.</returns>
         public PushedState PushOpacity(double opacity)
-            //TODO: Eliminate platform-specific push opacity call
+        //TODO: Eliminate platform-specific push opacity call
         {
-            _impl.PushOpacity(opacity);
+            PlatformImpl.PushOpacity(opacity);
             return new PushedState(this, PushedState.PushedStateType.Opacity);
         }
 
@@ -217,7 +246,7 @@ namespace Avalonia.Media
         /// <returns>A disposable to undo the opacity mask.</returns>
         public PushedState PushOpacityMask(IBrush mask, Rect bounds)
         {
-            _impl.PushOpacityMask(mask, bounds);
+            PlatformImpl.PushOpacityMask(mask, bounds);
             return new PushedState(this, PushedState.PushedStateType.OpacityMask);
         }
 
@@ -226,14 +255,14 @@ namespace Avalonia.Media
         /// </summary>
         /// <param name="matrix">The matrix</param>
         /// <returns>A disposable used to undo the transformation.</returns>
-        public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform*matrix);
+        public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
 
         /// <summary>
         /// Pushes a matrix pre-transformation.
         /// </summary>
         /// <param name="matrix">The matrix</param>
         /// <returns>A disposable used to undo the transformation.</returns>
-        public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix*CurrentTransform);
+        public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
 
         /// <summary>
         /// Sets the current matrix transformation.
@@ -244,7 +273,7 @@ namespace Avalonia.Media
         {
             var oldMatrix = CurrentTransform;
             CurrentTransform = matrix;
-            
+
             return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
         }
 
@@ -255,7 +284,7 @@ namespace Avalonia.Media
         public PushedState PushTransformContainer()
         {
             _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
-            _currentContainerTransform = CurrentTransform*_currentContainerTransform;
+            _currentContainerTransform = CurrentTransform * _currentContainerTransform;
             _currentTransform = Matrix.Identity;
             return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
         }
@@ -271,7 +300,12 @@ namespace Avalonia.Media
             _states = null;
             TransformStackPool.Push(_transformContainers);
             _transformContainers = null;
-            _impl.Dispose();
+            PlatformImpl.Dispose();
+        }
+
+        private static bool PenIsVisible(Pen pen)
+        {
+            return pen?.Brush != null && pen.Thickness > 0;
         }
     }
-}
+}

+ 12 - 6
src/Avalonia.Visuals/Media/IDrawingContext.cs → src/Avalonia.Visuals/Media/IDrawingContextImpl.cs

@@ -2,7 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using Avalonia.Media.Imaging;
+using Avalonia.Platform;
 
 namespace Avalonia.Media
 {
@@ -16,6 +16,12 @@ namespace Avalonia.Media
         /// </summary>
         Matrix Transform { get; set; }
 
+        /// <summary>
+        /// Clears the render target to the specified color.
+        /// </summary>
+        /// <param name="color">The color.</param>
+        void Clear(Color color);
+
         /// <summary>
         /// Draws a bitmap image.
         /// </summary>
@@ -23,7 +29,7 @@ namespace Avalonia.Media
         /// <param name="opacity">The opacity to draw with.</param>
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect);
+        void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect);
 
         /// <summary>
         /// Draws a line.
@@ -39,7 +45,7 @@ namespace Avalonia.Media
         /// <param name="brush">The fill brush.</param>
         /// <param name="pen">The stroke pen.</param>
         /// <param name="geometry">The geometry.</param>
-        void DrawGeometry(IBrush brush, Pen pen, Geometry geometry);
+        void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry);
 
         /// <summary>
         /// Draws the outline of a rectangle.
@@ -55,7 +61,7 @@ namespace Avalonia.Media
         /// <param name="foreground">The foreground brush.</param>
         /// <param name="origin">The upper-left corner of the text.</param>
         /// <param name="text">The text.</param>
-        void DrawText(IBrush foreground, Point origin, FormattedText text);
+        void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text);
 
         /// <summary>
         /// Draws a filled rectangle.
@@ -89,8 +95,8 @@ namespace Avalonia.Media
         /// Pushes a clip geometry.
         /// </summary>
         /// <param name="clip">The clip geometry.</param>
-        void PushGeometryClip(Geometry clip);
+        void PushGeometryClip(IGeometryImpl clip);
 
         void PopGeometryClip();
     }
-}
+}

+ 5 - 0
src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs

@@ -17,6 +17,11 @@ namespace Avalonia.Platform
         /// </summary>
         Size Constraint { get; set; }
 
+        /// <summary>
+        /// Gets the text.
+        /// </summary>
+        string Text { get; }
+
         /// <summary>
         /// Gets the lines in the text.
         /// </summary>

+ 15 - 10
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@@ -5,13 +5,12 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reactive.Disposables;
-using System.Runtime.InteropServices;
 using Avalonia.Cairo.Media.Imaging;
 using Avalonia.Media;
+using Avalonia.Platform;
 
 namespace Avalonia.Cairo.Media
 {
-    using Avalonia.Media.Imaging;
     using Cairo = global::Cairo;
 
     /// <summary>
@@ -60,6 +59,12 @@ namespace Avalonia.Cairo.Media
             }
         }
 
+        public void Clear(Color color)
+        {
+            _context.SetSourceRGBA(color.R, color.G, color.B, color.A);
+            _context.Paint();
+        }
+
         /// <summary>
         /// Ends a draw operation.
         /// </summary>
@@ -75,9 +80,9 @@ namespace Avalonia.Cairo.Media
         /// <param name="opacity">The opacity to draw with.</param>
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        public void DrawImage(IBitmap bitmap, double opacity, Rect sourceRect, Rect destRect)
+        public void DrawImage(IBitmapImpl bitmap, double opacity, Rect sourceRect, Rect destRect)
         {
-            var impl = bitmap.PlatformImpl as BitmapImpl;
+            var impl = bitmap as BitmapImpl;
             var size = new Size(impl.PixelWidth, impl.PixelHeight);
             var scale = new Vector(destRect.Width / sourceRect.Width, destRect.Height / sourceRect.Height);
 
@@ -137,9 +142,9 @@ namespace Avalonia.Cairo.Media
         /// <param name="brush">The fill brush.</param>
         /// <param name="pen">The stroke pen.</param>
         /// <param name="geometry">The geometry.</param>
-        public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
+        public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
         {
-            var impl = geometry.PlatformImpl as StreamGeometryImpl;
+            var impl = geometry as StreamGeometryImpl;
 
             var oldMatrix = Transform;
             Transform = impl.Transform * Transform;
@@ -192,9 +197,9 @@ namespace Avalonia.Cairo.Media
         /// <param name="foreground">The foreground brush.</param>
         /// <param name="origin">The upper-left corner of the text.</param>
         /// <param name="text">The text.</param>
-        public void DrawText(IBrush foreground, Point origin, FormattedText text)
+        public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
         {
-            var layout = ((FormattedTextImpl)text.PlatformImpl).Layout;
+            var layout = ((FormattedTextImpl)text).Layout;
             _context.MoveTo(origin.X, origin.Y);
 
             using (var b = SetBrush(foreground, new Size(0, 0))) 
@@ -350,10 +355,10 @@ namespace Avalonia.Cairo.Media
             return SetBrush(pen.Brush, destinationSize);
         }
 
-        public void PushGeometryClip(Geometry clip)
+        public void PushGeometryClip(IGeometryImpl clip)
         {
             _context.Save();
-            _context.AppendPath(((StreamGeometryImpl)clip.PlatformImpl).Path);
+            _context.AppendPath(((StreamGeometryImpl)clip).Path);
             _context.Clip();
         }
 

+ 4 - 4
src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs

@@ -13,7 +13,6 @@ namespace Avalonia.Cairo.Media
     public class FormattedTextImpl : IFormattedTextImpl
     {
         private Size _size;
-        private readonly string _text;
 
         static double CorrectScale(double input)
         {
@@ -32,7 +31,6 @@ namespace Avalonia.Cairo.Media
             Contract.Requires<ArgumentNullException>(context != null);
             Contract.Requires<ArgumentNullException>(text != null);
             Layout = new Pango.Layout(context);
-            _text = text;
             Layout.SetText(text);
             Layout.FontDescription = new Pango.FontDescription
             {
@@ -46,6 +44,8 @@ namespace Avalonia.Cairo.Media
             Layout.Attributes = new Pango.AttrList();
         }
 
+        public string Text => Layout.Text;
+
         public Size Constraint
         {
             get
@@ -99,7 +99,7 @@ namespace Avalonia.Cairo.Media
 
         int PangoIndexToTextIndex(int pangoIndex)
         {
-            return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(_text), 0, Math.Min(pangoIndex, _text.Length)).Length;
+            return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(Text), 0, Math.Min(pangoIndex, Text.Length)).Length;
         }
 
         public Rect HitTestTextPosition(int index)
@@ -109,7 +109,7 @@ namespace Avalonia.Cairo.Media
 
         int TextIndexToPangoIndex(int textIndex)
         {
-            return Encoding.UTF8.GetByteCount(textIndex < _text.Length ? _text.Remove(textIndex) : _text);
+            return Encoding.UTF8.GetByteCount(textIndex < Text.Length ? Text.Remove(textIndex) : Text);
         }
 
         public IEnumerable<Rect> HitTestTextRange(int index, int length)

+ 14 - 8
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -5,6 +5,7 @@ using SkiaSharp;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Avalonia.Platform;
 
 namespace Avalonia.Skia
 {
@@ -26,9 +27,14 @@ namespace Avalonia.Skia
             Transform = Matrix.Identity;
         }
 
-        public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
+        public void Clear(Color color)
         {
-            var impl = (BitmapImpl)source.PlatformImpl;
+            Canvas.Clear(color.ToSKColor());
+        }
+
+        public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+        {
+            var impl = (BitmapImpl)source;
             var s = sourceRect.ToSKRect();
             var d = destRect.ToSKRect();
             using (var paint = new SKPaint()
@@ -46,9 +52,9 @@ namespace Avalonia.Skia
             }
         }
 
-        public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
+        public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
         {
-            var impl = ((StreamGeometryImpl)geometry.PlatformImpl);
+            var impl = (StreamGeometryImpl)geometry;
             var size = geometry.Bounds.Size;
 
             using (var fill = brush != null ? CreatePaint(brush, size) : default(PaintWrapper))
@@ -284,11 +290,11 @@ namespace Avalonia.Skia
             }
         }
 
-        public void DrawText(IBrush foreground, Point origin, FormattedText text)
+        public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
         {
             using (var paint = CreatePaint(foreground, text.Measure()))
             {
-                var textImpl = text.PlatformImpl as FormattedTextImpl;
+                var textImpl = (FormattedTextImpl)text;
                 textImpl.Draw(this, Canvas, origin.ToSKPoint(), paint);
             }
         }
@@ -325,10 +331,10 @@ namespace Avalonia.Skia
                     disposable?.Dispose();
         }
 
-        public void PushGeometryClip(Geometry clip)
+        public void PushGeometryClip(IGeometryImpl clip)
         {
             Canvas.Save();
-            Canvas.ClipPath(((StreamGeometryImpl)clip.PlatformImpl).EffectivePath);
+            Canvas.ClipPath(((StreamGeometryImpl)clip).EffectivePath);
         }
 
         public void PopGeometryClip()

+ 15 - 14
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@@ -15,10 +15,10 @@ namespace Avalonia.Skia
         public FormattedTextImpl(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
                     TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping)
         {
-            _text = text ?? string.Empty;
+            Text = text ?? string.Empty;
 
             // Replace 0 characters with zero-width spaces (200B)
-            _text = _text.Replace((char)0, (char)0x200B);
+            Text = Text.Replace((char)0, (char)0x200B);
 
             var typeface = TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight);
 
@@ -54,6 +54,8 @@ namespace Avalonia.Skia
             }
         }
 
+        public string Text { get; }
+
         public void Dispose()
         {
         }
@@ -98,7 +100,7 @@ namespace Avalonia.Skia
                 {
                     IsInside = false,
                     TextPosition = line.Start + offset,
-                    IsTrailing = _text.Length == (line.Start + offset + 1)
+                    IsTrailing = Text.Length == (line.Start + offset + 1)
                 };
             }
 
@@ -108,7 +110,7 @@ namespace Avalonia.Skia
             {
                 IsInside = false,
                 IsTrailing = end,
-                TextPosition = end ? _text.Length - 1 : 0
+                TextPosition = end ? Text.Length - 1 : 0
             };
         }
 
@@ -182,7 +184,7 @@ namespace Avalonia.Skia
 
         public override string ToString()
         {
-            return _text;
+            return Text;
         }
 
         internal void Draw(DrawingContextImpl context,
@@ -231,7 +233,7 @@ namespace Avalonia.Skia
 
                     if (!hasCusomFGBrushes)
                     {
-                        var subString = _text.Substring(line.Start, line.Length);
+                        var subString = Text.Substring(line.Start, line.Length);
                         canvas.DrawText(subString, x, origin.Y + line.Top + _lineOffset, paint);
                     }
                     else
@@ -255,7 +257,7 @@ namespace Avalonia.Skia
                                 currentWrapper = foreground;
                             }
 
-                            subStr = _text.Substring(i, len);
+                            subStr = Text.Substring(i, len);
 
                             if (currFGPaint != currentWrapper.Paint)
                             {
@@ -284,7 +286,6 @@ namespace Avalonia.Skia
         private readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
         private readonly SKPaint _paint;
         private readonly List<Rect> _rects = new List<Rect>();
-        private readonly string _text;
         private readonly TextWrapping _wrapping;
         private Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
         private float _lineHeight = 0;
@@ -434,7 +435,7 @@ namespace Avalonia.Skia
 
                 for (int i = line.Start; i < line.Start + line.TextLength; i++)
                 {
-                    float w = _paint.MeasureText(_text[i].ToString());
+                    float w = _paint.MeasureText(Text[i].ToString());
 
                     _rects.Add(new Rect(
                         prevRight,
@@ -490,7 +491,7 @@ namespace Avalonia.Skia
 
         private List<Rect> GetRects()
         {
-            if (_text.Length > _rects.Count)
+            if (Text.Length > _rects.Count)
             {
                 BuildRects();
             }
@@ -500,7 +501,7 @@ namespace Avalonia.Skia
 
         private void Rebuild()
         {
-            var length = _text.Length;
+            var length = Text.Length;
 
             _lines.Clear();
             _rects.Clear();
@@ -536,7 +537,7 @@ namespace Avalonia.Skia
                 int measured;
                 int trailingnumber = 0;
 
-                subString = _text.Substring(curOff);
+                subString = Text.Substring(curOff);
 
                 float constraint = -1;
 
@@ -547,12 +548,12 @@ namespace Avalonia.Skia
                         constraint = MAX_LINE_WIDTH;
                 }
 
-                measured = LineBreak(_text, curOff, length, _paint, constraint, out trailingnumber);
+                measured = LineBreak(Text, curOff, length, _paint, constraint, out trailingnumber);
 
                 AvaloniaFormattedTextLine line = new AvaloniaFormattedTextLine();
                 line.TextLength = measured;
 
-                subString = _text.Substring(line.Start, line.TextLength);
+                subString = Text.Substring(line.Start, line.TextLength);
                 lineWidth = _paint.MeasureText(subString);
                 line.Start = curOff;
                 line.Length = measured - trailingnumber;

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@@ -65,7 +65,7 @@
     <Compile Include="HwndRenderTarget.cs" />
     <Compile Include="Media\BrushImpl.cs" />
     <Compile Include="Media\BrushWrapper.cs" />
-    <Compile Include="Media\DrawingContext.cs" />
+    <Compile Include="Media\DrawingContextImpl.cs" />
     <Compile Include="Media\Imaging\BitmapImpl.cs" />
     <Compile Include="Media\Imaging\D2DBitmapImpl.cs" />
     <Compile Include="Media\Imaging\RenderTargetBitmapImpl.cs" />

+ 2 - 2
src/Windows/Avalonia.Direct2D1/Media/AvaloniaTextRenderer.cs

@@ -11,14 +11,14 @@ namespace Avalonia.Direct2D1.Media
 {
     internal class AvaloniaTextRenderer : TextRenderer
     {
-        private readonly DrawingContext _context;
+        private readonly DrawingContextImpl _context;
 
         private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
 
         private readonly Brush _foreground;
 
         public AvaloniaTextRenderer(
-            DrawingContext context,
+            DrawingContextImpl context,
             SharpDX.Direct2D1.RenderTarget target,
             Brush foreground)
         {

+ 19 - 12
src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs → src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using Avalonia.Media;
+using Avalonia.Platform;
 using SharpDX;
 using SharpDX.Direct2D1;
 using SharpDX.Mathematics.Interop;
@@ -15,7 +16,7 @@ namespace Avalonia.Direct2D1.Media
     /// <summary>
     /// Draws using Direct2D1.
     /// </summary>
-    public class DrawingContext : IDrawingContextImpl, IDisposable
+    public class DrawingContextImpl : IDrawingContextImpl, IDisposable
     {
         /// <summary>
         /// The Direct2D1 render target.
@@ -30,12 +31,12 @@ namespace Avalonia.Direct2D1.Media
         private SharpDX.DXGI.SwapChain1 _swapChain;
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="DrawingContext"/> class.
+        /// Initializes a new instance of the <see cref="DrawingContextImpl"/> class.
         /// </summary>
         /// <param name="renderTarget">The render target to draw to.</param>
         /// <param name="directWriteFactory">The DirectWrite factory.</param>
         /// <param name="swapChain">An optional swap chain associated with this drawing context.</param>
-        public DrawingContext(
+        public DrawingContextImpl(
             SharpDX.Direct2D1.RenderTarget renderTarget,
             SharpDX.DirectWrite.Factory directWriteFactory,
             SharpDX.DXGI.SwapChain1 swapChain = null)
@@ -55,6 +56,12 @@ namespace Avalonia.Direct2D1.Media
             set { _renderTarget.Transform = value.ToDirect2D(); }
         }
 
+        /// <inheritdoc/>
+        public void Clear(Color color)
+        {
+            _renderTarget.Clear(color.ToDirect2D());
+        }
+
         /// <summary>
         /// Ends a draw operation.
         /// </summary>
@@ -81,9 +88,9 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="opacity">The opacity to draw with.</param>
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
+        public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
         {
-            var impl = (BitmapImpl)source.PlatformImpl;
+            var impl = (BitmapImpl)source;
             Bitmap d2d = impl.GetDirect2DBitmap(_renderTarget);
             _renderTarget.DrawBitmap(
                 d2d,
@@ -127,7 +134,7 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="brush">The fill brush.</param>
         /// <param name="pen">The stroke pen.</param>
         /// <param name="geometry">The geometry.</param>
-        public void DrawGeometry(IBrush brush, Pen pen, Avalonia.Media.Geometry geometry)
+        public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
         {
             if (brush != null)
             {
@@ -135,7 +142,7 @@ namespace Avalonia.Direct2D1.Media
                 {
                     if (d2dBrush.PlatformBrush != null)
                     {
-                        var impl = (GeometryImpl)geometry.PlatformImpl;
+                        var impl = (GeometryImpl)geometry;
                         _renderTarget.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush);
                     }
                 }
@@ -148,7 +155,7 @@ namespace Avalonia.Direct2D1.Media
                 {
                     if (d2dBrush.PlatformBrush != null)
                     {
-                        var impl = (GeometryImpl)geometry.PlatformImpl;
+                        var impl = (GeometryImpl)geometry;
                         _renderTarget.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke);
                     }
                 }
@@ -194,11 +201,11 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="foreground">The foreground brush.</param>
         /// <param name="origin">The upper-left corner of the text.</param>
         /// <param name="text">The text.</param>
-        public void DrawText(IBrush foreground, Point origin, FormattedText text)
+        public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
         {
             if (!string.IsNullOrEmpty(text.Text))
             {
-                var impl = (FormattedTextImpl)text.PlatformImpl;
+                var impl = (FormattedTextImpl)text;
 
                 using (var brush = CreateBrush(foreground, impl.Measure()))
                 using (var renderer = new AvaloniaTextRenderer(this, _renderTarget, brush.PlatformBrush))
@@ -343,14 +350,14 @@ namespace Avalonia.Direct2D1.Media
             }
         }
 
-        public void PushGeometryClip(Avalonia.Media.Geometry clip)
+        public void PushGeometryClip(IGeometryImpl clip)
         {
             var parameters = new LayerParameters
             {
                 ContentBounds = PrimitiveExtensions.RectangleInfinite,
                 MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
                 Opacity = 1,
-                GeometricMask = ((GeometryImpl)clip.PlatformImpl).Geometry
+                GeometricMask = ((GeometryImpl)clip).Geometry
             };
             var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget);
             _renderTarget.PushLayer(ref parameters, layer);

+ 3 - 0
src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

@@ -30,6 +30,7 @@ namespace Avalonia.Direct2D1.Media
                 (DWrite.FontStyle)fontStyle,
                 (float)fontSize))
             {
+                Text = text;
                 format.WordWrapping = wrapping == TextWrapping.Wrap ? 
                     DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap;
 
@@ -58,6 +59,8 @@ namespace Avalonia.Direct2D1.Media
             }
         }
 
+        public string Text { get; }
+
         public DWrite.TextLayout TextLayout { get; }
 
         public void Dispose()

+ 1 - 1
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@@ -51,7 +51,7 @@ namespace Avalonia.Direct2D1
         /// <returns>An <see cref="Avalonia.Media.DrawingContext"/>.</returns>
         public DrawingContext CreateDrawingContext()
         {
-            return new DrawingContext(new Media.DrawingContext(_renderTarget, DirectWriteFactory));
+            return new DrawingContext(new Media.DrawingContextImpl(_renderTarget, DirectWriteFactory));
         }
 
         public void Dispose()

+ 1 - 1
src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

@@ -69,7 +69,7 @@ namespace Avalonia.Direct2D1
                 CreateSwapChain();
             }
 
-            return new DrawingContext(new Media.DrawingContext(_deviceContext, DirectWriteFactory, _swapChain));
+            return new DrawingContext(new Media.DrawingContextImpl(_deviceContext, DirectWriteFactory, _swapChain));
         }
 
         public void Dispose()