Browse Source

Merge branch 'master' into properties-refactor

pull/387/merge
Steven Kirk 10 years ago
parent
commit
afe0f9012c
  1. 15
      docs/build.md
  2. 9
      samples/ControlCatalog/Pages/CanvasPage.paml
  3. 42
      samples/TestApplicationShared/MainWindow.cs
  4. 2
      src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
  5. 37
      src/Markup/Perspex.Markup.Xaml/Converters/PointsListTypeConverter.cs
  6. 1
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  7. 2
      src/Perspex.Controls/Perspex.Controls.csproj
  8. 40
      src/Perspex.Controls/Shapes/Line.cs
  9. 23
      src/Perspex.Controls/Shapes/Polygon.cs
  10. 28
      src/Perspex.Controls/Shapes/Polyline.cs
  11. 4
      src/Perspex.Controls/Shapes/Shape.cs
  12. 36
      src/Perspex.SceneGraph/Media/LineGeometry.cs
  13. 14
      src/Perspex.SceneGraph/Media/PathMarkupParser.cs
  14. 79
      src/Perspex.SceneGraph/Media/PolylineGeometry.cs
  15. 3
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  16. 20
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  17. 35
      src/Skia/Perspex.Skia/MethodTable.cs
  18. 59
      src/Skia/Perspex.Skia/PerspexHandleHolder.cs
  19. 40
      src/Skia/Perspex.Skia/StreamGeometryImpl.cs
  20. 10
      src/Skia/getnatives.sh
  21. 2
      tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj
  22. 2
      tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj
  23. 2
      tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj
  24. 43
      tests/Perspex.RenderTests/Shapes/LineTests.cs
  25. 12
      tests/Perspex.RenderTests/Shapes/PathTests.cs
  26. 76
      tests/Perspex.RenderTests/Shapes/PolygonTests.cs
  27. 83
      tests/Perspex.RenderTests/Shapes/PolylineTests.cs
  28. BIN
      tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png
  29. BIN
      tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png
  30. BIN
      tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png
  31. BIN
      tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png
  32. BIN
      tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png
  33. BIN
      tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png
  34. BIN
      tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png
  35. BIN
      tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png
  36. BIN
      tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png
  37. BIN
      tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png
  38. BIN
      tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png
  39. BIN
      tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png
  40. BIN
      tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png
  41. BIN
      tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png
  42. BIN
      tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png
  43. BIN
      tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png
  44. BIN
      tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png
  45. BIN
      tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png
  46. BIN
      tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png
  47. BIN
      tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png
  48. BIN
      tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png

15
docs/build.md

@ -7,8 +7,11 @@ Perspex requires Visual Studio 2015 to build on Windows.
### Install GTK Sharp
To compile the full project under windows, you must have [gtk-sharp](http://www.mono-project.com/download/#download-win) installed. However, if you're
not interested in building the cross-platform bits you can simply unload the Perspex.Cairo and
Perspex.Gtk project in Visual Studio.
not interested in building the cross-platform bits you can simply unload these projects from Visual Studio:
- Perspex.Cairo
- Perspex.Cairo.RenderTests
- Perspex.Gtk
### Clone the Perspex repository
@ -19,7 +22,7 @@ is linked as a submodule in the git repository, so run:
git submodule update --init
The next step is to download the Skia native libraries. Run ```getnatives.ps1``` PowerShell script which can be found under the folder ```Perspex\src\Skia\```.
The next step is to download the Skia native libraries. Run ```getnatives.ps1``` PowerShell script which can be found under the folder ```src\Skia\```.
## Linux
@ -39,14 +42,14 @@ Then install the needed packages:
### Clone the Perspex repository
git clone https://github.com/grokys/Perspex.git
git clone https://github.com/Perspex/Perspex.git
We currently need to build our own private version of ReactiveUI as it doesn't work on mono. This
is linked as a submodule in the git repository, so run:
git submodule update --init
The next step is to download the Skia native libraries. Run ```getnatives.sh``` script which can be found under the folder ```Perspex\src\Skia\```.
The next step is to download the Skia native libraries. Run ```getnatives.sh``` script which can be found under the folder ```src\Skia\```.
### Load the Project in MonoDevelop
@ -57,4 +60,4 @@ Set the TestApplication project as the startup project and click Run.
There will be some compile errors in the tests, but ignore them for now.
You can track the Linux version's progress in the [Linux issue](https://github.com/grokys/Perspex/issues/78).
You can track the Linux version's progress in the [Linux issue](https://github.com/Perspex/Perspex/issues/78).

9
samples/ControlCatalog/Pages/CanvasPage.paml

@ -2,12 +2,13 @@
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">Canvas</TextBlock>
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>
<Canvas Background="Yellow" Width="300" Height="200">
<Canvas Background="Yellow" Width="300" Height="400">
<Rectangle Fill="Blue" Width="63" Height="41" Canvas.Left="40" Canvas.Top="31"/>
<Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="160" Canvas.Top="79"/>
<Path Fill="Red" Data="M50,0 L0,50 100,50 Z" Canvas.Left="50" Canvas.Top="140"/>
<Path Fill="Orange" Data="M 0,0 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
<Line StartPoint="120,185" EndPoint="30,115" Stroke="Red" StrokeThickness="2"/>
<Polygon Points="75,0 120,120 0,45 150,45 30,120" Stroke="DarkBlue" StrokeThickness="1" Fill="Violet" Canvas.Left="150" Canvas.Top="180"/>
<Polyline Points="0,0 65,0 78,-26 91,39 104,-39 117,13 130,0 195,0" Stroke="Brown" Canvas.Left="30" Canvas.Top="350"/>
</Canvas>
</StackPanel>
</UserControl>

42
samples/TestApplicationShared/MainWindow.cs

@ -543,6 +543,17 @@ namespace TestApplication
private static TabItem LayoutTab()
{
var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
new Point(9, 1), new Point(10, 0), new Point(15, 0) };
var polygonPoints = new Point[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) };
for (int i = 0; i < polylinePoints.Length; i++)
{
polylinePoints[i] = polylinePoints[i] * 13;
}
for (int i = 0; i < polygonPoints.Length; i++)
{
polygonPoints[i] = polygonPoints[i] * 15;
}
return new TabItem
{
Header = "Layout",
@ -691,13 +702,36 @@ namespace TestApplication
},
new Line
{
Width = 90,
Height = 70,
Stroke = Brushes.Red,
StrokeThickness = 2,
StartPoint = new Point(120, 185),
EndPoint = new Point(30, 115)
},
new Perspex.Controls.Shapes.Path
{
Fill = Brushes.Orange,
Data = StreamGeometry.Parse("M 30,250 c 50,0 50,-50 c 50,0 50,50 h -50 v 50 l -50,-50 Z"),
},
new Polygon
{
Stroke = Brushes.DarkBlue,
Fill = Brushes.Violet,
Points = polygonPoints,
StrokeThickness = 1,
[Canvas.LeftProperty] = 150,
[Canvas.TopProperty] = 180,
},
new Polyline
{
Stroke = Brushes.Brown,
Points = polylinePoints,
StrokeThickness = 5,
StrokeJoin = PenLineJoin.Round,
StrokeStartLineCap = PenLineCap.Triangle,
StrokeEndLineCap = PenLineCap.Triangle,
[Canvas.LeftProperty] = 30,
[Canvas.TopProperty] = 120
}
[Canvas.TopProperty] = 350,
},
}
},
}

2
src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs

@ -21,6 +21,7 @@ using Perspex.Media.Imaging;
using Perspex.Metadata;
using Perspex.Platform;
using Perspex.Styling;
using System.Collections.Generic;
namespace Perspex.Markup.Xaml.Context
{
@ -101,6 +102,7 @@ namespace Perspex.Markup.Xaml.Context
new TypeConverterRegistration(typeof(PerspexList<double>), new PerspexListTypeConverter<double>()),
new TypeConverterRegistration(typeof(IMemberSelector), new MemberSelectorTypeConverter()),
new TypeConverterRegistration(typeof(Point), new PointTypeConverter()),
new TypeConverterRegistration(typeof(IList<Point>), new PointsListTypeConverter()),
new TypeConverterRegistration(typeof(PerspexProperty), new PerspexPropertyTypeConverter()),
new TypeConverterRegistration(typeof(RelativePoint), new RelativePointTypeConverter()),
new TypeConverterRegistration(typeof(RelativeRect), new RelativeRectTypeConverter()),

37
src/Markup/Perspex.Markup.Xaml/Converters/PointsListTypeConverter.cs

@ -0,0 +1,37 @@
using OmniXaml.TypeConversion;
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Perspex.Markup.Xaml.Converters
{
public class PointsListTypeConverter : ITypeConverter
{
public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
{
return false;
}
public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value)
{
string strValue = (string)value;
string[] pointStrs = strValue.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
var result = new List<Point>(pointStrs.Length);
foreach (var pointStr in pointStrs)
{
result.Add(Point.Parse(pointStr, culture));
}
return result;
}
public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
{
throw new NotImplementedException();
}
}
}

1
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -41,6 +41,7 @@
<Compile Include="Context\NameScopeWrapper.cs" />
<Compile Include="Converters\CursorTypeConverter.cs" />
<Compile Include="Converters\PerspexListTypeConverter.cs" />
<Compile Include="Converters\PointsListTypeConverter.cs" />
<Compile Include="Converters\RelativeRectTypeConverter.cs" />
<Compile Include="Data\MultiBinding.cs" />
<Compile Include="Data\RelativeSource.cs" />

2
src/Perspex.Controls/Perspex.Controls.csproj

@ -68,6 +68,8 @@
<Compile Include="SelectionMode.cs" />
<Compile Include="Separator.cs" />
<Compile Include="Shapes\Line.cs" />
<Compile Include="Shapes\Polygon.cs" />
<Compile Include="Shapes\Polyline.cs" />
<Compile Include="SystemDialog.cs" />
<Compile Include="Generators\ITreeItemContainerGenerator.cs" />
<Compile Include="Generators\ItemContainerEventArgs.cs" />

40
src/Perspex.Controls/Shapes/Line.cs

@ -9,28 +9,46 @@ namespace Perspex.Controls.Shapes
{
public class Line : Shape
{
private Geometry _geometry;
public static readonly PerspexProperty<Point> StartPointProperty =
PerspexProperty.Register<Line, Point>("StartPoint");
private Size _geometrySize;
public static readonly PerspexProperty<Point> EndPointProperty =
PerspexProperty.Register<Line, Point>("EndPoint");
private LineGeometry _geometry;
private Point _startPoint;
private Point _endPoint;
static Line()
{
StrokeThicknessProperty.OverrideDefaultValue<Line>(1);
}
public Point StartPoint
{
get { return GetValue(StartPointProperty); }
set { SetValue(StartPointProperty, value); }
}
public Point EndPoint
{
get { return GetValue(EndPointProperty); }
set { SetValue(EndPointProperty, value); }
}
public override Geometry DefiningGeometry
{
get
{
if (_geometry == null || _geometrySize != Bounds.Size)
if (_geometry == null || StartPoint != _startPoint || EndPoint != _endPoint)
{
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
_geometry = new LineGeometry(rect.TopLeft, rect.BottomRight);
_geometrySize = Bounds.Size;
_startPoint = StartPoint;
_endPoint = EndPoint;
_geometry = new LineGeometry(_startPoint, _endPoint);
}
return _geometry;
}
}
protected override Size MeasureOverride(Size availableSize)
{
return new Size(StrokeThickness, StrokeThickness);
}
}
}

23
src/Perspex.Controls/Shapes/Polygon.cs

@ -0,0 +1,23 @@
using Perspex.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Controls.Shapes
{
public class Polygon : Shape
{
public static readonly PerspexProperty<IList<Point>> PointsProperty =
PerspexProperty.Register<Polygon, IList<Point>>("Points");
public IList<Point> Points
{
get { return GetValue(PointsProperty); }
set { SetValue(PointsProperty, value); }
}
public override Geometry DefiningGeometry => new PolylineGeometry(Points, true);
}
}

28
src/Perspex.Controls/Shapes/Polyline.cs

@ -0,0 +1,28 @@
using Perspex.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Controls.Shapes
{
public class Polyline: Shape
{
public static readonly PerspexProperty<IList<Point>> PointsProperty =
PerspexProperty.Register<Polyline, IList<Point>>("Points");
static Polyline()
{
StrokeThicknessProperty.OverrideDefaultValue<Polyline>(1);
}
public IList<Point> Points
{
get { return GetValue(PointsProperty); }
set { SetValue(PointsProperty, value); }
}
public override Geometry DefiningGeometry => new PolylineGeometry(Points, false);
}
}

4
src/Perspex.Controls/Shapes/Shape.cs

@ -96,6 +96,8 @@ namespace Perspex.Controls.Shapes
public PenLineCap StrokeEndLineCap { get; set; } = PenLineCap.Flat;
public PenLineJoin StrokeJoin { get; set; } = PenLineJoin.Miter;
public override void Render(DrawingContext context)
{
var geometry = RenderedGeometry;
@ -103,7 +105,7 @@ namespace Perspex.Controls.Shapes
if (geometry != null)
{
var pen = new Pen(Stroke, StrokeThickness, new DashStyle(StrokeDashArray),
StrokeDashCap, StrokeStartLineCap, StrokeEndLineCap);
StrokeDashCap, StrokeStartLineCap, StrokeEndLineCap, StrokeJoin);
context.DrawGeometry(Fill, pen, geometry);
}
}

36
src/Perspex.SceneGraph/Media/LineGeometry.cs

@ -27,8 +27,8 @@ namespace Perspex.Media
using (IStreamGeometryContextImpl context = impl.Open())
{
context.BeginFigure(startPoint, false);
context.LineTo(endPoint);
context.BeginFigure(_startPoint, false);
context.LineTo(_endPoint);
context.EndFigure(false);
}
@ -36,12 +36,40 @@ namespace Perspex.Media
}
/// <inheritdoc/>
public override Rect Bounds => new Rect(_startPoint, _endPoint);
public override Rect Bounds
{
get
{
double xMin, yMin, xMax, yMax;
if (_startPoint.X <= _endPoint.X)
{
xMin = _startPoint.X;
xMax = _endPoint.X;
}
else
{
xMin = _endPoint.X;
xMax = _startPoint.X;
}
if (_startPoint.Y <= _endPoint.Y)
{
yMin = _startPoint.Y;
yMax = _endPoint.Y;
}
else
{
yMin = _endPoint.Y;
yMax = _startPoint.Y;
}
return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
}
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new LineGeometry(Bounds.TopLeft, Bounds.BottomRight);
return new LineGeometry(_startPoint, _endPoint);
}
}
}

14
src/Perspex.SceneGraph/Media/PathMarkupParser.cs

@ -147,6 +147,16 @@ namespace Perspex.Media
_context.CubicBezierTo(point1, point2, point);
break;
}
case Command.CubicBezierCurveRelative:
{
Point point1 = ReadRelativePoint(reader, point);
Point point2 = ReadRelativePoint(reader, point);
_context.CubicBezierTo(point, point1, point2);
point = point2;
break;
}
case Command.Arc:
{
//example: A10,10 0 0,0 10,20
@ -216,8 +226,10 @@ namespace Perspex.Media
}
}
private static double ReadDouble(TextReader reader)
private static double ReadDouble(StringReader reader)
{
ReadWhitespace(reader);
// TODO: Handle Infinity, NaN and scientific notation.
StringBuilder b = new StringBuilder();
bool readSign = false;

79
src/Perspex.SceneGraph/Media/PolylineGeometry.cs

@ -0,0 +1,79 @@
using Perspex.Platform;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perspex.Media
{
/// <summary>
/// Represents the geometry of an polyline or polygon.
/// </summary>
public class PolylineGeometry : Geometry
{
private IList<Point> _points;
private bool _isFilled;
public PolylineGeometry(IList<Point> points, bool isFilled)
{
_points = points;
_isFilled = isFilled;
IPlatformRenderInterface factory = PerspexLocator.Current.GetService<IPlatformRenderInterface>();
IStreamGeometryImpl impl = factory.CreateStreamGeometry();
using (IStreamGeometryContextImpl context = impl.Open())
{
if (points.Count > 0)
{
context.BeginFigure(points[0], isFilled);
for (int i = 1; i < points.Count; i++)
{
context.LineTo(points[i]);
}
context.EndFigure(isFilled);
}
}
PlatformImpl = impl;
}
/// <inheritdoc/>
public override Rect Bounds
{
get
{
double xMin = double.MaxValue, yMin = double.MaxValue;
double xMax = double.MinValue, yMax = double.MinValue;
foreach (var point in _points)
{
if (point.X < xMin)
{
xMin = point.X;
}
else if (point.X > xMax)
{
xMax = point.X;
}
if (point.Y < yMin)
{
yMin = point.Y;
}
else if (point.Y > yMax)
{
yMax = point.Y;
}
}
return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
}
}
/// <inheritdoc/>
public override Geometry Clone()
{
return new PolylineGeometry(new List<Point>(_points), _isFilled);
}
}
}

3
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -72,11 +72,12 @@
<Compile Include="Media\GradientSpreadMethod.cs" />
<Compile Include="Media\GradientStop.cs" />
<Compile Include="Media\LineGeometry.cs" />
<Compile Include="Media\PenLineJoin.cs" />
<Compile Include="Media\PolylineGeometry.cs" />
<Compile Include="Media\RadialGradientBrush.cs" />
<Compile Include="Media\LinearGradientBrush.cs" />
<Compile Include="Media\MediaExtensions.cs" />
<Compile Include="Media\PenLineCap.cs" />
<Compile Include="Media\PenLineJoin.cs" />
<Compile Include="Media\TextAlignment.cs" />
<Compile Include="Media\FontWeight.cs" />
<Compile Include="Media\FontStyle.cs" />

20
src/Skia/Perspex.Skia/DrawingContextImpl.cs

@ -37,18 +37,13 @@ namespace Perspex.Skia
public void DrawGeometry(Brush brush, Pen pen, Geometry geometry)
{
var impl = ((StreamGeometryImpl) geometry.PlatformImpl);
var oldTransform = Transform;
if (!impl.Transform.IsIdentity)
Transform = impl.Transform*Transform;
var size = geometry.Bounds.Size;
using(var fill = brush!=null?CreateBrush(brush, size):null)
using (var stroke = pen?.Brush != null ? CreateBrush(pen, size) : null)
{
MethodTable.Instance.DrawGeometry(Handle, impl.Path.Handle, fill != null ? fill.Brush : null,
MethodTable.Instance.DrawGeometry(Handle, impl.EffectivePath, fill != null ? fill.Brush : null,
stroke != null ? stroke.Brush : null, impl.FillRule == FillRule.EvenOdd);
}
Transform = oldTransform;
}
unsafe NativeBrushContainer CreateBrush(Brush brush, Size targetSize)
@ -56,7 +51,6 @@ namespace Perspex.Skia
var rv = NativeBrushPool.Instance.Get();
rv.Brush->Opacity = brush.Opacity;
var solid = brush as SolidColorBrush;
if (solid != null)
{
@ -108,8 +102,6 @@ namespace Perspex.Skia
rv.Brush->Bitmap = bitmap.Handle;
rv.Brush->BitmapTileMode = tileBrush.TileMode;
rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y);
}
return rv;
@ -121,6 +113,7 @@ namespace Perspex.Skia
brush.Brush->Stroke = true;
brush.Brush->StrokeThickness = (float)pen.Thickness;
brush.Brush->StrokeLineCap = pen.StartLineCap;
brush.Brush->StrokeLineJoin = pen.LineJoin;
brush.Brush->StrokeMiterLimit = (float)pen.MiterLimit;
if (pen.DashStyle?.Dashes != null)
@ -196,14 +189,7 @@ namespace Perspex.Skia
if(_currentTransform == value)
return;
_currentTransform = value;
_fmatrix[0] = (float)value.M11;
_fmatrix[1] = (float)value.M21;
_fmatrix[2] = (float)value.M31;
_fmatrix[3] = (float)value.M12;
_fmatrix[4] = (float)value.M22;
_fmatrix[5] = (float)value.M32;
MethodTable.Instance.SetTransform(Handle, _fmatrix);
MethodTable.Instance.SetTransform(Handle, value);
}
}
}

35
src/Skia/Perspex.Skia/MethodTable.cs

@ -47,9 +47,9 @@ namespace Perspex.Skia
public _PopClip PopClip;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void _SetTransform(IntPtr ctx, float[] matrix6);
public delegate void _SetTransform(IntPtr ctx, void* matrix6);
public _SetTransform SetTransform;
public _SetTransform SetTransformNative;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void _DrawLine(IntPtr ctx, void* brush, float x1, float y1, float x2, float y2);
@ -66,6 +66,11 @@ namespace Perspex.Skia
public _DisposePath DisposePath;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr _TransformPath(IntPtr path, void* matrix6);
public _TransformPath TransformPathNative;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void _DrawGeometry(IntPtr ctx, IntPtr path, void* fill, void* stroke, bool useEvenOdd);
@ -185,10 +190,34 @@ namespace Perspex.Skia
typeof (_RebuildFormattedText),
typeof (_DestroyFormattedText),
typeof (_DrawFormattedText),
typeof (_SetOption)
typeof (_SetOption),
typeof (_TransformPath)
};
void ConvertMatrix(Matrix value, float* target)
{
target[0] = (float)value.M11;
target[1] = (float)value.M21;
target[2] = (float)value.M31;
target[3] = (float)value.M12;
target[4] = (float)value.M22;
target[5] = (float)value.M32;
}
public unsafe IntPtr TransformPath(IntPtr path, Matrix matrix)
{
var tmp = stackalloc float[6];
ConvertMatrix(matrix, tmp);
return TransformPathNative(path, tmp);
}
public unsafe void SetTransform(IntPtr ctx, Matrix matrix)
{
var tmp = stackalloc float[6];
ConvertMatrix(matrix, tmp);
SetTransformNative(ctx, tmp);
}
protected MethodTable(IntPtr methodTable)
{

59
src/Skia/Perspex.Skia/PerspexHandleHolder.cs

@ -44,4 +44,63 @@ namespace Perspex.Skia
Dispose();
}
}
class RefCountable<T> : IDisposable where T : PerspexHandleHolder
{
class Shared
{
public readonly T Target;
private int _refCount = 1;
public Shared(T target)
{
Target = target;
}
public void AddRef() => _refCount++;
public void Release()
{
_refCount--;
if (_refCount <= 0)
Target.Dispose();
}
}
public bool IsDisposed => _shared == null;
private Shared _shared;
public void CheckDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
public IntPtr Handle
{
get
{
CheckDisposed();
return _shared.Target.Handle;
}
}
public RefCountable(T handle)
{
_shared = new Shared(handle);
}
public RefCountable(RefCountable<T> other)
{
other._shared.Target.CheckDisposed();
other._shared.AddRef();
_shared = other._shared;
}
public RefCountable<T> Clone() => new RefCountable<T>(this);
public void Dispose()
{
_shared?.Release();
_shared = null;
}
}
}

40
src/Skia/Perspex.Skia/StreamGeometryImpl.cs

@ -41,7 +41,11 @@ namespace Perspex.Skia
class StreamGeometryImpl : IStreamGeometryImpl
{
public SkPath Path;
RefCountable<SkPath> _path;
RefCountable<SkPath> _transformedPath;
private Matrix _transform = Matrix.Identity;
public IntPtr EffectivePath => (_transformedPath ?? _path).Handle;
public Rect GetRenderBounds(double strokeThickness)
{
@ -51,11 +55,35 @@ namespace Perspex.Skia
public Rect Bounds { get; private set; }
public Matrix Transform { get; set; } = Matrix.Identity;
public Matrix Transform
{
get { return _transform; }
set
{
if(_transform == value)
return;
_transform = value;
ApplyTransform();
}
}
void ApplyTransform()
{
if(_path == null)
return;
if (_transformedPath != null)
{
_transformedPath.Dispose();
_transformedPath = null;
}
if (!_transform.IsIdentity)
_transformedPath =
new RefCountable<SkPath>(new SkPath(MethodTable.Instance.TransformPath(_path.Handle, Transform)));
}
public IStreamGeometryImpl Clone()
{
return new StreamGeometryImpl() {Path = Path, Transform = Transform, Bounds = Bounds};
return new StreamGeometryImpl {_path = _path?.Clone(), _transformedPath = _transformedPath?.Clone(), _transform = Transform, Bounds = Bounds};
}
public IStreamGeometryContextImpl Open()
@ -77,7 +105,11 @@ namespace Perspex.Skia
{
var arr = _elements.ToArray();
SkRect rc;
_geometryImpl.Path = new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc));
_geometryImpl._path?.Dispose();
_geometryImpl._path =
new RefCountable<SkPath>(new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc)));
_geometryImpl.ApplyTransform();
_geometryImpl.Bounds = rc.ToRect();
}

10
src/Skia/getnatives.sh

@ -1,7 +1,13 @@
#!/bin/sh
rm -rf native
rm -rf native native.zip
mkdir -p native
cd native
if which curl
then
curl `cat ../native.url` -o native.zip
else
wget `cat ../native.url` -O native.zip
unzip native.zip
fi
unzip native.zip
chmod -R +x .

2
tests/Perspex.RenderTests/Perspex.Cairo.RenderTests.csproj

@ -74,6 +74,8 @@
<Compile Include="Shapes\LineTests.cs" />
<Compile Include="Shapes\PathTests.cs" />
<Compile Include="Shapes\EllipseTests.cs" />
<Compile Include="Shapes\PolygonTests.cs" />
<Compile Include="Shapes\PolylineTests.cs" />
<Compile Include="Shapes\RectangleTests.cs" />
<Compile Include="TestBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

2
tests/Perspex.RenderTests/Perspex.Direct2D1.RenderTests.csproj

@ -79,6 +79,8 @@
<Compile Include="Shapes\LineTests.cs" />
<Compile Include="Shapes\PathTests.cs" />
<Compile Include="Shapes\EllipseTests.cs" />
<Compile Include="Shapes\PolygonTests.cs" />
<Compile Include="Shapes\PolylineTests.cs" />
<Compile Include="Shapes\RectangleTests.cs" />
<Compile Include="TestBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

2
tests/Perspex.RenderTests/Perspex.Skia.RenderTests.csproj

@ -70,6 +70,8 @@
<Compile Include="Shapes\EllipseTests.cs" />
<Compile Include="Shapes\LineTests.cs" />
<Compile Include="Shapes\PathTests.cs" />
<Compile Include="Shapes\PolygonTests.cs" />
<Compile Include="Shapes\PolylineTests.cs" />
<Compile Include="Shapes\RectangleTests.cs" />
<Compile Include="TestBase.cs" />
</ItemGroup>

43
tests/Perspex.RenderTests/Shapes/LineTests.cs

@ -26,13 +26,54 @@ namespace Perspex.Direct2D1.RenderTests.Shapes
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Line
{
Stroke = Brushes.Black,
StrokeThickness = 1,
StartPoint = new Point(0, 0),
EndPoint = new Point(200, 200)
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void Line_1px_Stroke_Reversed()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Line
{
Stroke = Brushes.Black,
StrokeThickness = 1,
StartPoint = new Point(200, 0),
EndPoint = new Point(0, 200)
}
};
RenderToFile(target);
CompareImages();
}
[Fact]
public void Line_1px_Stroke_Vertical()
{
Decorator target = new Decorator
{
Width = 200,
Height = 200,
Child = new Line
{
Stroke = Brushes.Black,
StrokeThickness = 1,
StartPoint = new Point(100, 200),
EndPoint = new Point(100, 0)
}
};

12
tests/Perspex.RenderTests/Shapes/PathTests.cs

@ -46,11 +46,7 @@ namespace Perspex.Direct2D1.RenderTests.Shapes
CompareImages();
}
#if PERSPEX_SKIA
[Fact(Skip = "FIXME")]
#else
[Fact]
#endif
public void Path_Tick_Scaled()
{
Decorator target = new Decorator
@ -73,11 +69,7 @@ namespace Perspex.Direct2D1.RenderTests.Shapes
CompareImages();
}
#if PERSPEX_SKIA
[Fact(Skip = "FIXME")]
#else
[Fact]
#endif
public void Path_Tick_Scaled_Stroke_8px()
{
Decorator target = new Decorator
@ -100,11 +92,7 @@ namespace Perspex.Direct2D1.RenderTests.Shapes
CompareImages();
}
#if PERSPEX_SKIA
[Fact(Skip = "FIXME")]
#else
[Fact]
#endif
public void Path_Expander_With_Border()
{
Decorator target = new Decorator

76
tests/Perspex.RenderTests/Shapes/PolygonTests.cs

@ -0,0 +1,76 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Controls;
using Perspex.Controls.Shapes;
using Perspex.Media;
using Xunit;
#if PERSPEX_CAIRO
namespace Perspex.Cairo.RenderTests.Shapes
#elif PERSPEX_SKIA
namespace Perspex.Skia.RenderTests
#else
namespace Perspex.Direct2D1.RenderTests.Shapes
#endif
{
public class PolygonTests : TestBase
{
public PolygonTests()
: base(@"Shapes\Polygon")
{
}
#if PERSPEX_CAIRO
[Fact(Skip = "Caused by cairo bug")]
#else
[Fact]
#endif
public void Polygon_1px_Stroke()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 200,
Height = 200,
Child = new Polygon
{
Stroke = Brushes.DarkBlue,
Stretch = Stretch.Uniform,
Fill = Brushes.Violet,
Points = new [] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
StrokeThickness = 1
}
};
RenderToFile(target);
CompareImages();
}
#if PERSPEX_CAIRO
[Fact(Skip = "Caused by cairo bug")]
#else
[Fact]
#endif
public void Polygon_NonUniformFill()
{
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 400,
Height = 200,
Child = new Polygon
{
Stroke = Brushes.DarkBlue,
Stretch = Stretch.Fill,
Fill = Brushes.Violet,
Points = new[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
StrokeThickness = 5,
}
};
RenderToFile(target);
CompareImages();
}
}
}

83
tests/Perspex.RenderTests/Shapes/PolylineTests.cs

@ -0,0 +1,83 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Perspex.Controls;
using Perspex.Controls.Shapes;
using Perspex.Media;
using Xunit;
#if PERSPEX_CAIRO
namespace Perspex.Cairo.RenderTests.Shapes
#elif PERSPEX_SKIA
namespace Perspex.Skia.RenderTests
#else
namespace Perspex.Direct2D1.RenderTests.Shapes
#endif
{
public class PolylineTests : TestBase
{
public PolylineTests()
: base(@"Shapes\Polyline")
{
}
#if PERSPEX_CAIRO
[Fact(Skip = "Caused by cairo bug")]
#else
[Fact]
#endif
public void Polyline_1px_Stroke()
{
var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
new Point(9, 1), new Point(10, 0), new Point(15, 0) };
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 400,
Height = 200,
Child = new Polyline
{
Stroke = Brushes.Brown,
Points = polylinePoints,
Stretch = Stretch.Uniform,
StrokeThickness = 1
}
};
RenderToFile(target);
CompareImages();
}
#if PERSPEX_CAIRO
[Fact(Skip = "Caused by cairo bug")]
#else
[Fact]
#endif
public void Polyline_10px_Stroke_PenLineJoin()
{
var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
new Point(9, 1), new Point(10, 0), new Point(15, 0) };
Decorator target = new Decorator
{
Padding = new Thickness(8),
Width = 400,
Height = 200,
Child = new Polyline
{
Stroke = Brushes.Brown,
Points = polylinePoints,
Stretch = Stretch.Uniform,
StrokeJoin = PenLineJoin.Round,
StrokeStartLineCap = PenLineCap.Round,
StrokeEndLineCap = PenLineCap.Round,
StrokeThickness = 10
}
};
RenderToFile(target);
CompareImages();
}
}
}

BIN
tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke.expected.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 624 B

BIN
tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Reversed.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

BIN
tests/TestFiles/Cairo/Shapes/Line/Line_1px_Stroke_Vertical.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
tests/TestFiles/Cairo/Shapes/Polygon/Polygon_1px_Stroke.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
tests/TestFiles/Cairo/Shapes/Polygon/Polygon_NonUniformFill.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
tests/TestFiles/Cairo/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
tests/TestFiles/Cairo/Shapes/Polyline/Polyline_1px_Stroke.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke.expected.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 624 B

BIN
tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Reversed.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

BIN
tests/TestFiles/Direct2D1/Shapes/Line/Line_1px_Stroke_Vertical.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_1px_Stroke.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Polygon/Polygon_NonUniformFill.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Polyline/Polyline_1px_Stroke.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke.expected.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 624 B

BIN
tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Reversed.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

BIN
tests/TestFiles/Skia/Shapes/Line/Line_1px_Stroke_Vertical.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

BIN
tests/TestFiles/Skia/Shapes/Polygon/Polygon_1px_Stroke.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
tests/TestFiles/Skia/Shapes/Polygon/Polygon_NonUniformFill.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
tests/TestFiles/Skia/Shapes/Polyline/Polyline_10px_Stroke_PenLineJoin.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
tests/TestFiles/Skia/Shapes/Polyline/Polyline_1px_Stroke.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Loading…
Cancel
Save