diff --git a/src/Skia/Avalonia.Skia/GeometryImpl.cs b/src/Skia/Avalonia.Skia/GeometryImpl.cs index 0884399f4e..5042e6ebd2 100644 --- a/src/Skia/Avalonia.Skia/GeometryImpl.cs +++ b/src/Skia/Avalonia.Skia/GeometryImpl.cs @@ -11,12 +11,27 @@ namespace Avalonia.Skia internal abstract class GeometryImpl : IGeometryImpl { private PathCache _pathCache; - + private SKPathMeasure _pathMeasureCache; + /// public abstract Rect Bounds { get; } - + /// - public double ContourLength => _pathCache.CachePathMeasure?.Length ?? 0; + public double ContourLength + { + get + { + if (EffectivePath is null) + return 0; + + if (_pathMeasureCache is null) + { + _pathMeasureCache = new SKPathMeasure(EffectivePath); + } + + return (double)_pathMeasureCache?.Length; + } + } public abstract SKPath EffectivePath { get; } @@ -34,12 +49,12 @@ namespace Avalonia.Skia // Usually this function is being called with same stroke width per path, so this saves a lot of Skia traffic. var strokeWidth = (float)(pen?.Thickness ?? 0); - + if (!_pathCache.HasCacheFor(strokeWidth)) { UpdatePathCache(strokeWidth); } - + return PathContainsCore(_pathCache.CachedStrokePath, point); } @@ -62,7 +77,7 @@ namespace Avalonia.Skia { paint.IsStroke = true; paint.StrokeWidth = strokeWidth; - + paint.GetFillPath(EffectivePath, strokePath); _pathCache.Cache(strokePath, strokeWidth, strokePath.TightBounds.ToAvaloniaRect()); @@ -78,13 +93,13 @@ namespace Avalonia.Skia /// True, if point is contained in a path. private static bool PathContainsCore(SKPath path, Point point) { - return path.Contains((float)point.X, (float)point.Y); + return path.Contains((float)point.X, (float)point.Y); } /// public IGeometryImpl Intersect(IGeometryImpl geometry) { - var result = EffectivePath.Op(((GeometryImpl) geometry).EffectivePath, SKPathOp.Intersect); + var result = EffectivePath.Op(((GeometryImpl)geometry).EffectivePath, SKPathOp.Intersect); return result == null ? null : new StreamGeometryImpl(result); } @@ -93,70 +108,86 @@ namespace Avalonia.Skia public Rect GetRenderBounds(IPen pen) { var strokeWidth = (float)(pen?.Thickness ?? 0); - + if (!_pathCache.HasCacheFor(strokeWidth)) { UpdatePathCache(strokeWidth); } - + return _pathCache.CachedGeometryRenderBounds; } - + /// public ITransformedGeometryImpl WithTransform(Matrix transform) { return new TransformedGeometryImpl(this, transform); } - + /// public bool TryGetPointAtDistance(double distance, out Point point) { - if (_pathCache.CachePathMeasure is null) + if (EffectivePath is null) { point = new Point(); return false; } - - var res = _pathCache.CachePathMeasure.GetPosition((float)distance, out var skPoint); + + if (_pathMeasureCache is null) + { + _pathMeasureCache = new SKPathMeasure(EffectivePath); + } + + var res = _pathMeasureCache.GetPosition((float)distance, out var skPoint); point = new Point(skPoint.X, skPoint.Y); return res; } - + /// public bool TryGetPointAndTangentAtDistance(double distance, out Point point, out Point tangent) { - if (_pathCache.CachePathMeasure is null) + if (EffectivePath is null) { point = new Point(); tangent = new Point(); return false; } - - var res = _pathCache.CachePathMeasure.GetPositionAndTangent((float)distance, out var skPoint, out var skTangent); + + if (_pathMeasureCache is null) + { + _pathMeasureCache = new SKPathMeasure(EffectivePath); + } + + var res = _pathMeasureCache.GetPositionAndTangent((float)distance, out var skPoint, out var skTangent); point = new Point(skPoint.X, skPoint.Y); tangent = new Point(skTangent.X, skTangent.Y); return res; } - public bool TryGetSegment(float startDistance, float stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry) + public bool TryGetSegment(float startDistance, float stopDistance, bool startOnBeginFigure, + out IGeometryImpl segmentGeometry) { - if (_pathCache.CachePathMeasure is null) + if (EffectivePath is null) { segmentGeometry = null; return false; } + + if (_pathMeasureCache is null) + { + _pathMeasureCache = new SKPathMeasure(EffectivePath); + } segmentGeometry = null; SKPath _skPathSegment = null; - var res = _pathCache.CachePathMeasure.GetSegment(startDistance, stopDistance, _skPathSegment, startOnBeginFigure); + var res = _pathMeasureCache.GetSegment(startDistance, stopDistance, _skPathSegment, startOnBeginFigure); if (res) { segmentGeometry = new StreamGeometryImpl(_skPathSegment); } - + return res; } @@ -176,7 +207,7 @@ namespace Avalonia.Skia /// Tolerance for two stroke widths to be deemed equal /// public const float Tolerance = float.Epsilon; - + /// /// Cached contour path. /// @@ -186,11 +217,6 @@ namespace Avalonia.Skia /// Cached geometry render bounds. /// public Rect CachedGeometryRenderBounds { get; private set; } - - /// - /// Cached path measurement helper. - /// - public SKPathMeasure CachePathMeasure { get; private set; } /// /// Is cached valid for given stroke width. @@ -216,8 +242,7 @@ namespace Avalonia.Skia } CachedStrokePath = path; - CachedGeometryRenderBounds = geometryRenderBounds; - CachePathMeasure = new SKPathMeasure(path); + CachedGeometryRenderBounds = geometryRenderBounds; _cachedStrokeWidth = strokeWidth; } @@ -227,7 +252,6 @@ namespace Avalonia.Skia public void Invalidate() { CachedStrokePath?.Dispose(); - CachePathMeasure?.Dispose(); CachedGeometryRenderBounds = Rect.Empty; _cachedStrokeWidth = default(float); }