Browse Source

Platform geometry implementations for D2D and Skia.

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

3
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
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
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
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
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
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
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);
}
}
}

31
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);
}
}
}

10
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);
}
}
}

28
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
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
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
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
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
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
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
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
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;
}
}
}

8
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
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
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
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());
}
}
}

2
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
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
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();

Loading…
Cancel
Save