Browse Source

Platform geometry implementations for D2D and Skia.

Dariusz Komosiński 6 years ago
parent
commit
8534769435
25 changed files with 398 additions and 62 deletions
  1. 3 0
      samples/ControlCatalog/MainView.xaml
  2. 23 0
      samples/ControlCatalog/Pages/EllipsePage.xaml
  3. 18 0
      samples/ControlCatalog/Pages/EllipsePage.xaml.cs
  4. 21 0
      samples/ControlCatalog/Pages/LinePage.xaml
  5. 18 0
      samples/ControlCatalog/Pages/LinePage.xaml.cs
  6. 21 0
      samples/ControlCatalog/Pages/RectanglePage.xaml
  7. 18 0
      samples/ControlCatalog/Pages/RectanglePage.xaml.cs
  8. 1 30
      src/Avalonia.Visuals/Media/EllipseGeometry.cs
  9. 1 9
      src/Avalonia.Visuals/Media/LineGeometry.cs
  10. 10 18
      src/Avalonia.Visuals/Media/RectangleGeometry.cs
  11. 12 0
      src/Avalonia.Visuals/Platform/IEllipseGeometryImpl.cs
  12. 12 0
      src/Avalonia.Visuals/Platform/ILineGeometryImpl.cs
  13. 22 0
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  14. 12 0
      src/Avalonia.Visuals/Platform/IRectangleGeometryImpl.cs
  15. 26 0
      src/Skia/Avalonia.Skia/EllipseGeometryImpl.cs
  16. 30 0
      src/Skia/Avalonia.Skia/LineGeometryImpl.cs
  17. 6 0
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  18. 26 0
      src/Skia/Avalonia.Skia/RectangleGeometryImpl.cs
  19. 4 4
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  20. 28 0
      src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs
  21. 28 0
      src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs
  22. 27 0
      src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs
  23. 1 1
      src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
  24. 15 0
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
  25. 15 0
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

+ 3 - 0
samples/ControlCatalog/MainView.xaml

@@ -23,14 +23,17 @@
       <TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
       <TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
       <TabItem Header="DropDown"><pages:DropDownPage/></TabItem>
+      <TabItem Header="Ellipse"><pages:EllipsePage/></TabItem>
       <TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
       <TabItem Header="Image"><pages:ImagePage/></TabItem>
       <TabItem Header="LayoutTransformControl"><pages:LayoutTransformControlPage/></TabItem>
+      <TabItem Header="Line"><pages:LinePage/></TabItem>
       <TabItem Header="ListBox"><pages:ListBoxPage/></TabItem>
       <TabItem Header="Menu"><pages:MenuPage/></TabItem>
 	  <TabItem Header="NumericUpDown"><pages:NumericUpDownPage/></TabItem>
       <TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
       <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
+      <TabItem Header="Rectangle"><pages:RectanglePage/></TabItem>
       <TabItem Header="Slider"><pages:SliderPage/></TabItem>
       <TabItem Header="TabControl"><pages:TabControlPage/></TabItem>
       <TabItem Header="TextBox"><pages:TextBoxPage/></TabItem>

+ 23 - 0
samples/ControlCatalog/Pages/EllipsePage.xaml

@@ -0,0 +1,23 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="ControlCatalog.Pages.EllipsePage">
+  <StackPanel Orientation="Vertical" Spacing="4">
+    <TextBlock Classes="h1">Ellipse</TextBlock>
+    <TextBlock Classes="h2">A control which represents an ellipse with a fill and a stroke.</TextBlock>
+
+    <StackPanel Orientation="Vertical"
+                Margin="0,16,0,0"
+                HorizontalAlignment="Center"
+                Spacing="16">
+      <Ellipse Width="200" Height="200" Fill="Red" Stroke="Blue" StrokeThickness="{Binding #StrokeThickness.Value}"/>
+
+      <Ellipse Width="200" Height="100" Fill="Red" Stroke="Blue" StrokeThickness="{Binding #StrokeThickness.Value}"/>
+
+      <Grid ColumnDefinitions="Auto,*">
+        <TextBlock Grid.Column="0" Text="Stroke thickness"/>
+        <Slider Grid.Column="1"  Name="StrokeThickness" Maximum="10" Value="1"/>
+      </Grid>
+      
+    </StackPanel>
+  </StackPanel>
+</UserControl>

+ 18 - 0
samples/ControlCatalog/Pages/EllipsePage.xaml.cs

@@ -0,0 +1,18 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+    public class EllipsePage : UserControl
+    {
+        public EllipsePage()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+}

+ 21 - 0
samples/ControlCatalog/Pages/LinePage.xaml

@@ -0,0 +1,21 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="ControlCatalog.Pages.LinePage">
+  <StackPanel Orientation="Vertical" Spacing="4">
+    <TextBlock Classes="h1">Line</TextBlock>
+    <TextBlock Classes="h2">A control which represents a line.</TextBlock>
+
+    <StackPanel Orientation="Vertical"
+                Margin="0,16,0,0"
+                HorizontalAlignment="Center"
+                Spacing="16">
+      <Line StartPoint="0,0" EndPoint="200,100" Stroke="Blue" StrokeThickness="{Binding #StrokeThickness.Value}"/>
+
+      <Grid ColumnDefinitions="Auto,*">
+        <TextBlock Grid.Column="0" Text="Stroke thickness"/>
+        <Slider Grid.Column="1"  Name="StrokeThickness" Maximum="10" Value="1"/>
+      </Grid>
+      
+    </StackPanel>
+  </StackPanel>
+</UserControl>

+ 18 - 0
samples/ControlCatalog/Pages/LinePage.xaml.cs

@@ -0,0 +1,18 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+    public class LinePage : UserControl
+    {
+        public LinePage()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+}

+ 21 - 0
samples/ControlCatalog/Pages/RectanglePage.xaml

@@ -0,0 +1,21 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="ControlCatalog.Pages.RectanglePage">
+  <StackPanel Orientation="Vertical" Spacing="4">
+    <TextBlock Classes="h1">Rectangle</TextBlock>
+    <TextBlock Classes="h2">A control which represents a rectangle with a fill and a stroke.</TextBlock>
+
+    <StackPanel Orientation="Vertical"
+                Margin="0,16,0,0"
+                HorizontalAlignment="Center"
+                Spacing="16">
+      <Rectangle Width="200" Height="32" Fill="Red" Stroke="Blue" StrokeThickness="{Binding #StrokeThickness.Value}"/>
+
+      <Grid ColumnDefinitions="Auto,*">
+          <TextBlock Grid.Column="0" Text="Stroke thickness"/>
+          <Slider Grid.Column="1"  Name="StrokeThickness" Maximum="10" Value="1"/>
+      </Grid>
+      
+    </StackPanel>
+  </StackPanel>
+</UserControl>

+ 18 - 0
samples/ControlCatalog/Pages/RectanglePage.xaml.cs

@@ -0,0 +1,18 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+    public class RectanglePage : UserControl
+    {
+        public RectanglePage()
+        {
+            this.InitializeComponent();
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+        }
+    }
+}

+ 1 - 30
src/Avalonia.Visuals/Media/EllipseGeometry.cs

@@ -1,7 +1,6 @@
 // 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 Avalonia.Platform;
 
 namespace Avalonia.Media
@@ -57,36 +56,8 @@ namespace Avalonia.Media
         protected override IGeometryImpl CreateDefiningGeometry()
         {
             var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
-            var geometry = factory.CreateStreamGeometry();
 
-            using (var ctx = geometry.Open())
-            {
-                var rect = Rect;
-                double controlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3;
-                var center = rect.Center;
-                var radius = new Vector(rect.Width / 2, rect.Height / 2);
-
-                var x0 = center.X - radius.X;
-                var x1 = center.X - (radius.X * controlPointRatio);
-                var x2 = center.X;
-                var x3 = center.X + (radius.X * controlPointRatio);
-                var x4 = center.X + radius.X;
-
-                var y0 = center.Y - radius.Y;
-                var y1 = center.Y - (radius.Y * controlPointRatio);
-                var y2 = center.Y;
-                var y3 = center.Y + (radius.Y * controlPointRatio);
-                var y4 = center.Y + radius.Y;
-
-                ctx.BeginFigure(new Point(x2, y0), true);
-                ctx.CubicBezierTo(new Point(x3, y0), new Point(x4, y1), new Point(x4, y2));
-                ctx.CubicBezierTo(new Point(x4, y3), new Point(x3, y4), new Point(x2, y4));
-                ctx.CubicBezierTo(new Point(x1, y4), new Point(x0, y3), new Point(x0, y2));
-                ctx.CubicBezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0));
-                ctx.EndFigure(true);
-            }
-
-            return geometry;
+            return factory.CreateEllipseGeometry(Rect);
         }
     }
 }

+ 1 - 9
src/Avalonia.Visuals/Media/LineGeometry.cs

@@ -73,16 +73,8 @@ namespace Avalonia.Media
         protected override IGeometryImpl CreateDefiningGeometry()
         {
             var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
-            var geometry = factory.CreateStreamGeometry();
 
-            using (var context = geometry.Open())
-            {
-                context.BeginFigure(StartPoint, false);
-                context.LineTo(EndPoint);
-                context.EndFigure(false);
-            }
-
-            return geometry;
+            return factory.CreateLineGeometry(StartPoint, EndPoint);
         }
     }
 }

+ 10 - 18
src/Avalonia.Visuals/Media/RectangleGeometry.cs

@@ -16,12 +16,6 @@ namespace Avalonia.Media
         public static readonly StyledProperty<Rect> RectProperty =
             AvaloniaProperty.Register<RectangleGeometry, Rect>(nameof(Rect));
 
-        public Rect Rect
-        {
-            get => GetValue(RectProperty);
-            set => SetValue(RectProperty, value);
-        }
-
         static RectangleGeometry()
         {
             AffectsGeometry(RectProperty);
@@ -43,25 +37,23 @@ namespace Avalonia.Media
             Rect = rect;
         }
 
+        /// <summary>
+        /// Gets or sets the bounds of the rectangle.
+        /// </summary>
+        public Rect Rect
+        {
+            get => GetValue(RectProperty);
+            set => SetValue(RectProperty, value);
+        }
+
         /// <inheritdoc/>
         public override Geometry Clone() => new RectangleGeometry(Rect);
 
         protected override IGeometryImpl CreateDefiningGeometry()
         {
             var factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
-            var geometry = factory.CreateStreamGeometry();
-
-            using (var context = geometry.Open())
-            {
-                var rect = Rect;
-                context.BeginFigure(rect.TopLeft, true);
-                context.LineTo(rect.TopRight);
-                context.LineTo(rect.BottomRight);
-                context.LineTo(rect.BottomLeft);
-                context.EndFigure(true);
-            }
 
-            return geometry;
+            return factory.CreateRectangleGeometry(Rect);
         }
     }
 }

+ 12 - 0
src/Avalonia.Visuals/Platform/IEllipseGeometryImpl.cs

@@ -0,0 +1,12 @@
+// 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.
+
+namespace Avalonia.Platform
+{
+    /// <summary>
+    /// Defines the platform-specific interface for a <see cref="Avalonia.Media.EllipseGeometry"/>.
+    /// </summary>
+    public interface IEllipseGeometryImpl : IGeometryImpl
+    {
+    }
+}

+ 12 - 0
src/Avalonia.Visuals/Platform/ILineGeometryImpl.cs

@@ -0,0 +1,12 @@
+// 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.
+
+namespace Avalonia.Platform
+{
+    /// <summary>
+    /// Defines the platform-specific interface for a <see cref="Avalonia.Media.LineGeometry"/>.
+    /// </summary>
+    public interface ILineGeometryImpl : IGeometryImpl
+    {
+    }
+}

+ 22 - 0
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@@ -36,6 +36,28 @@ namespace Avalonia.Platform
             Size constraint,
             IReadOnlyList<FormattedTextStyleSpan> spans);
 
+        /// <summary>
+        /// Creates an ellipse geometry implementation.
+        /// </summary>
+        /// <param name="rect">The bounds of the ellipse.</param>
+        /// <returns>An <see cref="IEllipseGeometryImpl"/>.</returns>
+        IEllipseGeometryImpl CreateEllipseGeometry(Rect rect);
+
+        /// <summary>
+        /// Creates a line geometry implementation.
+        /// </summary>
+        /// <param name="p1">The start of the line.</param>
+        /// <param name="p2">The end of the line.</param>
+        /// <returns>An <see cref="ILineGeometryImpl"/>.</returns>
+        ILineGeometryImpl CreateLineGeometry(Point p1, Point p2);
+
+        /// <summary>
+        /// Creates a rectangle geometry implementation.
+        /// </summary>
+        /// <param name="rect">The bounds of the rectangle.</param>
+        /// <returns>An <see cref="IRectangleGeometryImpl"/>.</returns>
+        IRectangleGeometryImpl CreateRectangleGeometry(Rect rect);
+
         /// <summary>
         /// Creates a stream geometry implementation.
         /// </summary>

+ 12 - 0
src/Avalonia.Visuals/Platform/IRectangleGeometryImpl.cs

@@ -0,0 +1,12 @@
+// 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.
+
+namespace Avalonia.Platform
+{
+    /// <summary>
+    /// Defines the platform-specific interface for a <see cref="Avalonia.Media.RectangleGeometry"/>.
+    /// </summary>
+    public interface IRectangleGeometryImpl : IGeometryImpl
+    {
+    }
+}

+ 26 - 0
src/Skia/Avalonia.Skia/EllipseGeometryImpl.cs

@@ -0,0 +1,26 @@
+// 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 Avalonia.Platform;
+using SkiaSharp;
+
+namespace Avalonia.Skia
+{
+    /// <summary>
+    /// A Skia implementation of a <see cref="Avalonia.Media.EllipseGeometry"/>.
+    /// </summary>
+    internal class EllipseGeometryImpl : GeometryImpl, IEllipseGeometryImpl
+    {
+        public override Rect Bounds { get; }
+        public override SKPath EffectivePath { get; }
+
+        public EllipseGeometryImpl(Rect rect)
+        {
+            var path = new SKPath();
+            path.AddOval(rect.ToSKRect());
+
+            EffectivePath = path;
+            Bounds = rect;
+        }
+    }
+}

+ 30 - 0
src/Skia/Avalonia.Skia/LineGeometryImpl.cs

@@ -0,0 +1,30 @@
+// 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 Avalonia.Platform;
+using SkiaSharp;
+
+namespace Avalonia.Skia
+{
+    /// <summary>
+    /// A Skia implementation of a <see cref="Avalonia.Media.LineGeometry"/>.
+    /// </summary>
+    internal class LineGeometryImpl : GeometryImpl, ILineGeometryImpl
+    {
+        public override Rect Bounds { get; }
+        public override SKPath EffectivePath { get; }
+
+        public LineGeometryImpl(Point p1, Point p2)
+        {
+            var path = new SKPath();
+            path.MoveTo(p1.ToSKPoint());
+            path.LineTo(p2.ToSKPoint());
+
+            EffectivePath = path;
+            Bounds = new Rect(
+                new Point(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y)), 
+                new Point(Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y)));
+        }
+    }
+}

+ 6 - 0
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@@ -50,6 +50,12 @@ namespace Avalonia.Skia
             return new FormattedTextImpl(text, typeface, textAlignment, wrapping, constraint, spans);
         }
 
+        public IEllipseGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
+
+        public ILineGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
+
+        public IRectangleGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect);
+
         /// <inheritdoc />
         public IStreamGeometryImpl CreateStreamGeometry()
         {

+ 26 - 0
src/Skia/Avalonia.Skia/RectangleGeometryImpl.cs

@@ -0,0 +1,26 @@
+// 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 Avalonia.Platform;
+using SkiaSharp;
+
+namespace Avalonia.Skia
+{
+    /// <summary>
+    /// A Skia implementation of a <see cref="Avalonia.Media.RectangleGeometry"/>.
+    /// </summary>
+    internal class RectangleGeometryImpl : GeometryImpl, IRectangleGeometryImpl
+    {
+        public override Rect Bounds { get; }
+        public override SKPath EffectivePath { get; }
+
+        public RectangleGeometryImpl(Rect rect)
+        {
+            var path = new SKPath();
+            path.AddRect(rect.ToSKRect());
+
+            EffectivePath = path;
+            Bounds = rect;
+        }
+    }
+}

+ 4 - 4
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@@ -182,10 +182,10 @@ namespace Avalonia.Direct2D1
             return new WriteableWicBitmapImpl(size, dpi, format);
         }
 
-        public IStreamGeometryImpl CreateStreamGeometry()
-        {
-            return new StreamGeometryImpl();
-        }
+        public IEllipseGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
+        public ILineGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
+        public IRectangleGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect);
+        public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl();
 
         public IBitmapImpl LoadBitmap(string fileName)
         {

+ 28 - 0
src/Windows/Avalonia.Direct2D1/Media/EllipseGeometryImpl.cs

@@ -0,0 +1,28 @@
+// 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 Avalonia.Platform;
+using SharpDX.Direct2D1;
+
+namespace Avalonia.Direct2D1.Media
+{
+    /// <summary>
+    /// A Direct2D implementation of a <see cref="Avalonia.Media.EllipseGeometry"/>.
+    /// </summary>
+    internal class EllipseGeometryImpl : GeometryImpl, IEllipseGeometryImpl
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
+        /// </summary>
+        public EllipseGeometryImpl(Rect rect)
+            : base(CreateGeometry(rect))
+        {
+        }
+
+        private static Geometry CreateGeometry(Rect rect)
+        {
+            var ellipse = new Ellipse(rect.Center.ToSharpDX(), (float)rect.Width / 2, (float)rect.Height / 2);
+            return new EllipseGeometry(Direct2D1Platform.Direct2D1Factory, ellipse);
+        }
+    }
+}

+ 28 - 0
src/Windows/Avalonia.Direct2D1/Media/LineGeometryImpl.cs

@@ -0,0 +1,28 @@
+// 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 Avalonia.Platform;
+using SharpDX.Direct2D1;
+
+namespace Avalonia.Direct2D1.Media
+{
+    /// <summary>
+    /// A Direct2D implementation of a <see cref="Avalonia.Media.LineGeometry"/>.
+    /// </summary>
+    internal class LineGeometryImpl : StreamGeometryImpl, ILineGeometryImpl
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
+        /// </summary>
+        public LineGeometryImpl(Point p1, Point p2)
+        {
+            using (var sink = ((PathGeometry)Geometry).Open())
+            {
+                sink.BeginFigure(p1.ToSharpDX(), FigureBegin.Hollow);
+                sink.AddLine(p2.ToSharpDX());
+                sink.EndFigure(FigureEnd.Open);
+                sink.Close();
+            }
+        }
+    }
+}

+ 27 - 0
src/Windows/Avalonia.Direct2D1/Media/RectangleGeometryImpl.cs

@@ -0,0 +1,27 @@
+// 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 Avalonia.Platform;
+using SharpDX.Direct2D1;
+
+namespace Avalonia.Direct2D1.Media
+{
+    /// <summary>
+    /// A Direct2D implementation of a <see cref="Avalonia.Media.RectangleGeometry"/>.
+    /// </summary>
+    internal class RectangleGeometryImpl : GeometryImpl, IRectangleGeometryImpl
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="StreamGeometryImpl"/> class.
+        /// </summary>
+        public RectangleGeometryImpl(Rect rect)
+            : base(CreateGeometry(rect))
+        {
+        }
+
+        private static Geometry CreateGeometry(Rect rect)
+        {
+            return new RectangleGeometry(Direct2D1Platform.Direct2D1Factory, rect.ToDirect2D());
+        }
+    }
+}

+ 1 - 1
src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs

@@ -50,7 +50,7 @@ namespace Avalonia.Win32.Interop.Wpf
                 {
                     _resource = texture.QueryInterface<SharpDX.Direct3D11.Resource>();
                     
-                    Target = new RenderTarget(AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Factory>(), surface,
+                    Target = new RenderTarget(Direct2D1Platform.Direct2D1Factory, surface,
                         new RenderTargetProperties
                         {
                             DpiX = (float) dpi.X,

+ 15 - 0
tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

@@ -22,6 +22,21 @@ namespace Avalonia.UnitTests
             return Mock.Of<IFormattedTextImpl>();
         }
 
+        public IEllipseGeometryImpl CreateEllipseGeometry(Rect rect)
+        {
+            throw new NotImplementedException();
+        }
+
+        public ILineGeometryImpl CreateLineGeometry(Point p1, Point p2)
+        {
+            throw new NotImplementedException();
+        }
+
+        public IRectangleGeometryImpl CreateRectangleGeometry(Rect rect)
+        {
+            throw new NotImplementedException();
+        }
+
         public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
         {
             return Mock.Of<IRenderTarget>();

+ 15 - 0
tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

@@ -56,6 +56,21 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
             throw new NotImplementedException();
         }
 
+        public IEllipseGeometryImpl CreateEllipseGeometry(Rect rect)
+        {
+            throw new NotImplementedException();
+        }
+
+        public ILineGeometryImpl CreateLineGeometry(Point p1, Point p2)
+        {
+            throw new NotImplementedException();
+        }
+
+        public IRectangleGeometryImpl CreateRectangleGeometry(Rect rect)
+        {
+            throw new NotImplementedException();
+        }
+
         class MockStreamGeometry : IStreamGeometryImpl
         {
             private MockStreamGeometryContext _impl = new MockStreamGeometryContext();