diff --git a/src/Skia/Perspex.Skia/DrawingContextImpl.cs b/src/Skia/Perspex.Skia/DrawingContextImpl.cs index 405f021515..cce05bd1ae 100644 --- a/src/Skia/Perspex.Skia/DrawingContextImpl.cs +++ b/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) @@ -196,14 +191,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); } } } diff --git a/src/Skia/Perspex.Skia/MethodTable.cs b/src/Skia/Perspex.Skia/MethodTable.cs index ba21588333..d17ead8720 100644 --- a/src/Skia/Perspex.Skia/MethodTable.cs +++ b/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) { diff --git a/src/Skia/Perspex.Skia/PerspexHandleHolder.cs b/src/Skia/Perspex.Skia/PerspexHandleHolder.cs index d8f52e8f61..158a2f768c 100644 --- a/src/Skia/Perspex.Skia/PerspexHandleHolder.cs +++ b/src/Skia/Perspex.Skia/PerspexHandleHolder.cs @@ -44,4 +44,63 @@ namespace Perspex.Skia Dispose(); } } + + class RefCountable : 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 other) + { + other._shared.Target.CheckDisposed(); + other._shared.AddRef(); + _shared = other._shared; + } + + public RefCountable Clone() => new RefCountable(this); + + public void Dispose() + { + _shared?.Release(); + _shared = null; + } + } } \ No newline at end of file diff --git a/src/Skia/Perspex.Skia/StreamGeometryImpl.cs b/src/Skia/Perspex.Skia/StreamGeometryImpl.cs index 3c62e65e7d..292af3d651 100644 --- a/src/Skia/Perspex.Skia/StreamGeometryImpl.cs +++ b/src/Skia/Perspex.Skia/StreamGeometryImpl.cs @@ -41,7 +41,11 @@ namespace Perspex.Skia class StreamGeometryImpl : IStreamGeometryImpl { - public SkPath Path; + RefCountable _path; + RefCountable _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(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(new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc))); + _geometryImpl.ApplyTransform(); + _geometryImpl.Bounds = rc.ToRect(); } diff --git a/tests/Perspex.RenderTests/Shapes/PathTests.cs b/tests/Perspex.RenderTests/Shapes/PathTests.cs index 68b5956234..3d52b87ac2 100644 --- a/tests/Perspex.RenderTests/Shapes/PathTests.cs +++ b/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