@@ -43,46 +43,11 @@ public bool FillContains(Point point)
43
43
/// <inheritdoc />
44
44
public bool StrokeContains ( IPen ? pen , Point point )
45
45
{
46
- // Skia requires to compute stroke path to check for point containment.
47
- // Due to that we are caching using stroke width.
48
- // Usually this function is being called with same stroke width per path, so this saves a lot of Skia traffic.
46
+ _pathCache . UpdateIfNeeded ( StrokePath , pen ) ;
49
47
50
- var strokeWidth = ( float ) ( pen ? . Thickness ?? 0 ) ;
51
-
52
- if ( ! _pathCache . HasCacheFor ( strokeWidth ) )
53
- {
54
- UpdatePathCache ( strokeWidth ) ;
55
- }
56
-
57
- return PathContainsCore ( _pathCache . CachedStrokePath , point ) ;
58
- }
59
-
60
- /// <summary>
61
- /// Update path cache for given stroke width.
62
- /// </summary>
63
- /// <param name="strokeWidth">Stroke width.</param>
64
- private void UpdatePathCache ( float strokeWidth )
65
- {
66
- var strokePath = new SKPath ( ) ;
67
-
68
- // For stroke widths close to 0 simply use empty path. Render bounds are cached from fill path.
69
- if ( Math . Abs ( strokeWidth ) < float . Epsilon )
70
- {
71
- _pathCache . Cache ( strokePath , strokeWidth , Bounds ) ;
72
- }
73
- else
74
- {
75
- var paint = SKPaintCache . Shared . Get ( ) ;
76
- paint . IsStroke = true ;
77
- paint . StrokeWidth = strokeWidth ;
78
- paint . GetFillPath ( StrokePath , strokePath ) ;
79
-
80
- SKPaintCache . Shared . ReturnReset ( paint ) ;
81
-
82
- _pathCache . Cache ( strokePath , strokeWidth , strokePath . TightBounds . ToAvaloniaRect ( ) ) ;
83
- }
48
+ return PathContainsCore ( _pathCache . ExpandedPath , point ) ;
84
49
}
85
-
50
+
86
51
/// <summary>
87
52
/// Check Skia path if it contains a point.
88
53
/// </summary>
@@ -106,14 +71,8 @@ private static bool PathContainsCore(SKPath? path, Point point)
106
71
/// <inheritdoc />
107
72
public Rect GetRenderBounds ( IPen ? pen )
108
73
{
109
- var strokeWidth = ( float ) ( pen ? . Thickness ?? 0 ) ;
110
-
111
- if ( ! _pathCache . HasCacheFor ( strokeWidth ) )
112
- {
113
- UpdatePathCache ( strokeWidth ) ;
114
- }
115
-
116
- return _pathCache . CachedGeometryRenderBounds ;
74
+ _pathCache . UpdateIfNeeded ( StrokePath , pen ) ;
75
+ return _pathCache . RenderBounds ;
117
76
}
118
77
119
78
/// <inheritdoc />
@@ -180,66 +139,70 @@ public bool TryGetSegment(double startDistance, double stopDistance, bool startO
180
139
/// </summary>
181
140
protected void InvalidateCaches ( )
182
141
{
183
- _pathCache . Invalidate ( ) ;
142
+ _pathCache . Dispose ( ) ;
143
+ _pathCache = default ;
184
144
}
185
145
186
146
private struct PathCache
187
147
{
188
- private float _cachedStrokeWidth ;
189
-
190
- /// <summary>
191
- /// Tolerance for two stroke widths to be deemed equal
192
- /// </summary>
193
- public const float Tolerance = float . Epsilon ;
194
-
195
- /// <summary>
196
- /// Cached contour path.
197
- /// </summary>
198
- public SKPath ? CachedStrokePath { get ; private set ; }
199
-
200
- /// <summary>
201
- /// Cached geometry render bounds.
202
- /// </summary>
203
- public Rect CachedGeometryRenderBounds { get ; private set ; }
204
-
205
- /// <summary>
206
- /// Is cached valid for given stroke width.
207
- /// </summary>
208
- /// <param name="strokeWidth">Stroke width to check.</param>
209
- /// <returns>True, if CachedStrokePath can be used for given stroke width.</returns>
210
- public bool HasCacheFor ( float strokeWidth )
148
+ private double _width , _miterLimit ;
149
+ private PenLineCap _cap ;
150
+ private PenLineJoin _join ;
151
+ private SKPath ? _path , _cachedFor ;
152
+ private Rect ? _renderBounds ;
153
+ private static readonly SKPath s_emptyPath = new ( ) ;
154
+
155
+
156
+ public Rect RenderBounds => _renderBounds ??= ( _path ?? _cachedFor ?? s_emptyPath ) . Bounds . ToAvaloniaRect ( ) ;
157
+ public SKPath ExpandedPath => _path ?? s_emptyPath ;
158
+
159
+ public void UpdateIfNeeded ( SKPath ? strokePath , IPen ? pen )
211
160
{
212
- return CachedStrokePath != null && Math . Abs ( _cachedStrokeWidth - strokeWidth ) < Tolerance ;
213
- }
214
-
215
- /// <summary>
216
- /// Cache path for given stroke width. Takes ownership of a passed path.
217
- /// </summary>
218
- /// <param name="path">Path to cache.</param>
219
- /// <param name="strokeWidth">Stroke width to cache.</param>
220
- /// <param name="geometryRenderBounds">Render bounds to use.</param>
221
- public void Cache ( SKPath path , float strokeWidth , Rect geometryRenderBounds )
222
- {
223
- if ( CachedStrokePath != path )
161
+ var strokeWidth = pen ? . Thickness ?? 0 ;
162
+ var miterLimit = pen ? . MiterLimit ?? 0 ;
163
+ var cap = pen ? . LineCap ?? default ;
164
+ var join = pen ? . LineJoin ?? default ;
165
+
166
+ if ( _cachedFor == strokePath
167
+ && _path != null
168
+ && cap == _cap
169
+ && join == _join
170
+ && Math . Abs ( _width - strokeWidth ) < float . Epsilon
171
+ && ( join != PenLineJoin . Miter || Math . Abs ( _miterLimit - miterLimit ) > float . Epsilon ) )
172
+ // We are up to date
173
+ return ;
174
+
175
+ _renderBounds = null ;
176
+ _cachedFor = strokePath ;
177
+ _width = strokeWidth ;
178
+ _cap = cap ;
179
+ _join = join ;
180
+ _miterLimit = miterLimit ;
181
+
182
+ if ( strokePath == null || Math . Abs ( strokeWidth ) < float . Epsilon )
224
183
{
225
- CachedStrokePath ? . Dispose ( ) ;
184
+ _path = null ;
185
+ return ;
226
186
}
227
187
228
- CachedStrokePath = path ;
229
- CachedGeometryRenderBounds = geometryRenderBounds ;
230
- _cachedStrokeWidth = strokeWidth ;
188
+ var paint = SKPaintCache . Shared . Get ( ) ;
189
+ paint . IsStroke = true ;
190
+ paint . StrokeWidth = ( float ) _width ;
191
+ paint . StrokeCap = cap . ToSKStrokeCap ( ) ;
192
+ paint . StrokeJoin = join . ToSKStrokeJoin ( ) ;
193
+ paint . StrokeMiter = ( float ) miterLimit ;
194
+ _path = new SKPath ( ) ;
195
+ paint . GetFillPath ( strokePath , _path ) ;
196
+
197
+ SKPaintCache . Shared . ReturnReset ( paint ) ;
231
198
}
232
199
233
- /// <summary>
234
- /// Invalidate cache state.
235
- /// </summary>
236
- public void Invalidate ( )
200
+ public void Dispose ( )
237
201
{
238
- CachedStrokePath ? . Dispose ( ) ;
239
- CachedStrokePath = null ;
240
- CachedGeometryRenderBounds = default ;
241
- _cachedStrokeWidth = default ;
202
+ _path ? . Dispose ( ) ;
203
+ _path = null ;
242
204
}
205
+
243
206
}
244
207
}
245
208
}
0 commit comments