/src/skia/src/core/SkPathPriv.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2015 Google Inc. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #ifndef SkPathPriv_DEFINED |
9 | | #define SkPathPriv_DEFINED |
10 | | |
11 | | #include "include/core/SkArc.h" |
12 | | #include "include/core/SkPath.h" |
13 | | #include "include/core/SkPathBuilder.h" |
14 | | #include "include/core/SkPathTypes.h" |
15 | | #include "include/core/SkPoint.h" |
16 | | #include "include/core/SkRect.h" |
17 | | #include "include/core/SkRefCnt.h" |
18 | | #include "include/core/SkScalar.h" |
19 | | #include "include/core/SkTypes.h" |
20 | | #include "include/private/SkIDChangeListener.h" |
21 | | #include "include/private/SkPathRef.h" |
22 | | #include "include/private/base/SkDebug.h" |
23 | | #include "src/core/SkPathEnums.h" |
24 | | |
25 | | #include <cstdint> |
26 | | #include <iterator> |
27 | | #include <utility> |
28 | | |
29 | | class SkMatrix; |
30 | | class SkRRect; |
31 | | |
32 | | static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch"); |
33 | | static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch"); |
34 | | static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch"); |
35 | | static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch"); |
36 | | |
37 | | class SkPathPriv { |
38 | | public: |
39 | | // skbug.com/9906: Not a perfect solution for W plane clipping, but 1/16384 is a |
40 | | // reasonable limit (roughly 5e-5) |
41 | | inline static constexpr SkScalar kW0PlaneDistance = 1.f / (1 << 14); |
42 | | |
43 | 0 | static SkPathFirstDirection AsFirstDirection(SkPathDirection dir) { |
44 | 0 | // since we agree numerically for the values in Direction, we can just cast. |
45 | 0 | return (SkPathFirstDirection)dir; |
46 | 0 | } |
47 | | |
48 | | /** |
49 | | * Return the opposite of the specified direction. kUnknown is its own |
50 | | * opposite. |
51 | | */ |
52 | 1.08k | static SkPathFirstDirection OppositeFirstDirection(SkPathFirstDirection dir) { |
53 | 1.08k | static const SkPathFirstDirection gOppositeDir[] = { |
54 | 1.08k | SkPathFirstDirection::kCCW, SkPathFirstDirection::kCW, SkPathFirstDirection::kUnknown, |
55 | 1.08k | }; |
56 | 1.08k | return gOppositeDir[(unsigned)dir]; |
57 | 1.08k | } |
58 | | |
59 | | /** |
60 | | * Tries to compute the direction of the outer-most non-degenerate |
61 | | * contour. If it can be computed, return that direction. If it cannot be determined, |
62 | | * or the contour is known to be convex, return kUnknown. If the direction was determined, |
63 | | * it is cached to make subsequent calls return quickly. |
64 | | */ |
65 | | static SkPathFirstDirection ComputeFirstDirection(const SkPath&); |
66 | | |
67 | 343 | static bool IsClosedSingleContour(const SkPath& path) { |
68 | 343 | int verbCount = path.countVerbs(); |
69 | 343 | if (verbCount == 0) |
70 | 0 | return false; |
71 | 343 | int moveCount = 0; |
72 | 343 | auto verbs = path.fPathRef->verbsBegin(); |
73 | 4.15k | for (int i = 0; i < verbCount; i++) { |
74 | 4.14k | switch (verbs[i]) { |
75 | 611 | case SkPath::Verb::kMove_Verb: |
76 | 611 | moveCount += 1; |
77 | 611 | if (moveCount > 1) { |
78 | 268 | return false; |
79 | 268 | } |
80 | 343 | break; |
81 | 343 | case SkPath::Verb::kClose_Verb: |
82 | 57 | if (i == verbCount - 1) { |
83 | 0 | return true; |
84 | 0 | } |
85 | 57 | return false; |
86 | 3.47k | default: break; |
87 | 4.14k | } |
88 | 4.14k | } |
89 | 18 | return false; |
90 | 343 | } |
91 | | |
92 | | // In some scenarios (e.g. fill or convexity checking all but the last leading move to are |
93 | | // irrelevant to behavior). SkPath::injectMoveToIfNeeded should ensure that this is always at |
94 | | // least 1. |
95 | 14.0M | static int LeadingMoveToCount(const SkPath& path) { |
96 | 14.0M | int verbCount = path.countVerbs(); |
97 | 14.0M | auto verbs = path.fPathRef->verbsBegin(); |
98 | 41.1M | for (int i = 0; i < verbCount; i++) { |
99 | 40.8M | if (verbs[i] != SkPath::Verb::kMove_Verb) { |
100 | 13.7M | return i; |
101 | 13.7M | } |
102 | 40.8M | } |
103 | 285k | return verbCount; // path is all move verbs |
104 | 14.0M | } |
105 | | |
106 | 361 | static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) { |
107 | 361 | path.fPathRef->addGenIDChangeListener(std::move(listener)); |
108 | 361 | } |
109 | | |
110 | | /** |
111 | | * This returns true for a rect that has a move followed by 3 or 4 lines and a close. If |
112 | | * 'isSimpleFill' is true, an uncloseed rect will also be accepted as long as it starts and |
113 | | * ends at the same corner. This does not permit degenerate line or point rectangles. |
114 | | */ |
115 | | static bool IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect, |
116 | | SkPathDirection* direction, unsigned* start); |
117 | | |
118 | | /** |
119 | | * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function |
120 | | * assumes empty ovals and zero sweeps have already been filtered out. |
121 | | */ |
122 | | static void CreateDrawArcPath(SkPath* path, const SkArc& arc, bool isFillNoPathEffect); |
123 | | |
124 | | /** |
125 | | * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty |
126 | | * oval. |
127 | | */ |
128 | | static bool DrawArcIsConvex(SkScalar sweepAngle, SkArc::Type arcType, bool isFillNoPathEffect); |
129 | | |
130 | 6.17M | static void ShrinkToFit(SkPath* path) { |
131 | 6.17M | path->shrinkToFit(); |
132 | 6.17M | } |
133 | | |
134 | | /** |
135 | | * Returns a C++11-iterable object that traverses a path's verbs in order. e.g: |
136 | | * |
137 | | * for (SkPath::Verb verb : SkPathPriv::Verbs(path)) { |
138 | | * ... |
139 | | * } |
140 | | */ |
141 | | struct Verbs { |
142 | | public: |
143 | 0 | Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {} |
144 | | struct Iter { |
145 | 0 | void operator++() { fVerb++; } |
146 | 0 | bool operator!=(const Iter& b) { return fVerb != b.fVerb; } |
147 | 0 | SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); } |
148 | | const uint8_t* fVerb; |
149 | | }; |
150 | 0 | Iter begin() { return Iter{fPathRef->verbsBegin()}; } |
151 | 0 | Iter end() { return Iter{fPathRef->verbsEnd()}; } |
152 | | private: |
153 | | Verbs(const Verbs&) = delete; |
154 | | Verbs& operator=(const Verbs&) = delete; |
155 | | SkPathRef* fPathRef; |
156 | | }; |
157 | | |
158 | | /** |
159 | | * Iterates through a raw range of path verbs, points, and conics. All values are returned |
160 | | * unaltered. |
161 | | * |
162 | | * NOTE: This class's definition will be moved into SkPathPriv once RangeIter is removed. |
163 | | */ |
164 | | using RangeIter = SkPath::RangeIter; |
165 | | |
166 | | /** |
167 | | * Iterable object for traversing verbs, points, and conic weights in a path: |
168 | | * |
169 | | * for (auto [verb, pts, weights] : SkPathPriv::Iterate(skPath)) { |
170 | | * ... |
171 | | * } |
172 | | */ |
173 | | struct Iterate { |
174 | | public: |
175 | | Iterate(const SkPath& path) |
176 | | : Iterate(path.fPathRef->verbsBegin(), |
177 | | // Don't allow iteration through non-finite points. |
178 | | (!path.isFinite()) ? path.fPathRef->verbsBegin() |
179 | | : path.fPathRef->verbsEnd(), |
180 | 20.6M | path.fPathRef->points(), path.fPathRef->conicWeights()) { |
181 | 20.6M | } |
182 | | Iterate(const uint8_t* verbsBegin, const uint8_t* verbsEnd, const SkPoint* points, |
183 | | const SkScalar* weights) |
184 | 20.6M | : fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) { |
185 | 20.6M | } |
186 | 12.9M | SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; } |
187 | 19.0M | SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; } |
188 | | private: |
189 | | const uint8_t* fVerbsBegin; |
190 | | const uint8_t* fVerbsEnd; |
191 | | const SkPoint* fPoints; |
192 | | const SkScalar* fWeights; |
193 | | }; |
194 | | |
195 | | /** |
196 | | * Returns a pointer to the verb data. |
197 | | */ |
198 | 9.26k | static const uint8_t* VerbData(const SkPath& path) { |
199 | 9.26k | return path.fPathRef->verbsBegin(); |
200 | 9.26k | } |
201 | | |
202 | | /** Returns a raw pointer to the path points */ |
203 | 9.26k | static const SkPoint* PointData(const SkPath& path) { |
204 | 9.26k | return path.fPathRef->points(); |
205 | 9.26k | } |
206 | | |
207 | | /** Returns the number of conic weights in the path */ |
208 | 54.8k | static int ConicWeightCnt(const SkPath& path) { |
209 | 54.8k | return path.fPathRef->countWeights(); |
210 | 54.8k | } |
211 | | |
212 | | /** Returns a raw pointer to the path conic weights. */ |
213 | 9.26k | static const SkScalar* ConicWeightData(const SkPath& path) { |
214 | 9.26k | return path.fPathRef->conicWeights(); |
215 | 9.26k | } |
216 | | |
217 | | /** Returns true if the underlying SkPathRef has one single owner. */ |
218 | 0 | static bool TestingOnly_unique(const SkPath& path) { |
219 | 0 | return path.fPathRef->unique(); |
220 | 0 | } |
221 | | |
222 | | // Won't be needed once we can make path's immutable (with their bounds always computed) |
223 | 0 | static bool HasComputedBounds(const SkPath& path) { |
224 | 0 | return path.hasComputedBounds(); |
225 | 0 | } |
226 | | |
227 | | /** Returns true if constructed by addCircle(), addOval(); and in some cases, |
228 | | addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not |
229 | | return true though SkPath draws oval. |
230 | | |
231 | | rect receives bounds of oval. |
232 | | dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if |
233 | | counterclockwise. |
234 | | start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left. |
235 | | |
236 | | rect, dir, and start are unmodified if oval is not found. |
237 | | |
238 | | Triggers performance optimizations on some GPU surface implementations. |
239 | | |
240 | | @param rect storage for bounding SkRect of oval; may be nullptr |
241 | | @param dir storage for SkPathDirection; may be nullptr |
242 | | @param start storage for start of oval; may be nullptr |
243 | | @return true if SkPath was constructed by method that reduces to oval |
244 | | */ |
245 | 165k | static bool IsOval(const SkPath& path, SkRect* rect, SkPathDirection* dir, unsigned* start) { |
246 | 165k | bool isCCW = false; |
247 | 165k | bool result = path.fPathRef->isOval(rect, &isCCW, start); |
248 | 165k | if (dir && result) { |
249 | 19 | *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; |
250 | 19 | } |
251 | 165k | return result; |
252 | 165k | } |
253 | | |
254 | | /** Returns true if constructed by addRoundRect(), addRRect(); and if construction |
255 | | is not empty, not SkRect, and not oval. SkPath constructed with other calls |
256 | | will not return true though SkPath draws SkRRect. |
257 | | |
258 | | rrect receives bounds of SkRRect. |
259 | | dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if |
260 | | counterclockwise. |
261 | | start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left. |
262 | | |
263 | | rrect, dir, and start are unmodified if SkRRect is not found. |
264 | | |
265 | | Triggers performance optimizations on some GPU surface implementations. |
266 | | |
267 | | @param rrect storage for bounding SkRect of SkRRect; may be nullptr |
268 | | @param dir storage for SkPathDirection; may be nullptr |
269 | | @param start storage for start of SkRRect; may be nullptr |
270 | | @return true if SkPath contains only SkRRect |
271 | | */ |
272 | | static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPathDirection* dir, |
273 | 165k | unsigned* start) { |
274 | 165k | bool isCCW = false; |
275 | 165k | bool result = path.fPathRef->isRRect(rrect, &isCCW, start); |
276 | 165k | if (dir && result) { |
277 | 446 | *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; |
278 | 446 | } |
279 | 165k | return result; |
280 | 165k | } |
281 | | |
282 | | /** |
283 | | * Sometimes in the drawing pipeline, we have to perform math on path coordinates, even after |
284 | | * the path is in device-coordinates. Tessellation and clipping are two examples. Usually this |
285 | | * is pretty modest, but it can involve subtracting/adding coordinates, or multiplying by |
286 | | * small constants (e.g. 2,3,4). To try to preflight issues where these optionations could turn |
287 | | * finite path values into infinities (or NaNs), we allow the upper drawing code to reject |
288 | | * the path if its bounds (in device coordinates) is too close to max float. |
289 | | */ |
290 | 349k | static bool TooBigForMath(const SkRect& bounds) { |
291 | | // This value is just a guess. smaller is safer, but we don't want to reject largish paths |
292 | | // that we don't have to. |
293 | 349k | constexpr SkScalar scale_down_to_allow_for_small_multiplies = 0.25f; |
294 | 349k | constexpr SkScalar max = SK_ScalarMax * scale_down_to_allow_for_small_multiplies; |
295 | | |
296 | | // use ! expression so we return true if bounds contains NaN |
297 | 349k | return !(bounds.fLeft >= -max && bounds.fTop >= -max && |
298 | 349k | bounds.fRight <= max && bounds.fBottom <= max); |
299 | 349k | } |
300 | 327k | static bool TooBigForMath(const SkPath& path) { |
301 | 327k | return TooBigForMath(path.getBounds()); |
302 | 327k | } |
303 | | |
304 | | // Returns number of valid points for each SkPath::Iter verb |
305 | 119M | static int PtsInIter(unsigned verb) { |
306 | 119M | static const uint8_t gPtsInVerb[] = { |
307 | 119M | 1, // kMove pts[0] |
308 | 119M | 2, // kLine pts[0..1] |
309 | 119M | 3, // kQuad pts[0..2] |
310 | 119M | 3, // kConic pts[0..2] |
311 | 119M | 4, // kCubic pts[0..3] |
312 | 119M | 0, // kClose |
313 | 119M | 0 // kDone |
314 | 119M | }; |
315 | | |
316 | 119M | SkASSERT(verb < std::size(gPtsInVerb)); |
317 | 119M | return gPtsInVerb[verb]; |
318 | 119M | } |
319 | | |
320 | | // Returns number of valid points for each verb, not including the "starter" |
321 | | // point that the Iterator adds for line/quad/conic/cubic |
322 | 108M | static int PtsInVerb(unsigned verb) { |
323 | 108M | static const uint8_t gPtsInVerb[] = { |
324 | 108M | 1, // kMove pts[0] |
325 | 108M | 1, // kLine pts[0..1] |
326 | 108M | 2, // kQuad pts[0..2] |
327 | 108M | 2, // kConic pts[0..2] |
328 | 108M | 3, // kCubic pts[0..3] |
329 | 108M | 0, // kClose |
330 | 108M | 0 // kDone |
331 | 108M | }; |
332 | | |
333 | 108M | SkASSERT(verb < std::size(gPtsInVerb)); |
334 | 108M | return gPtsInVerb[verb]; |
335 | 108M | } |
336 | | |
337 | | static bool IsAxisAligned(const SkPath& path); |
338 | | |
339 | 14.1k | static bool AllPointsEq(const SkPoint pts[], int count) { |
340 | 19.3k | for (int i = 1; i < count; ++i) { |
341 | 17.4k | if (pts[0] != pts[i]) { |
342 | 12.2k | return false; |
343 | 12.2k | } |
344 | 17.4k | } |
345 | 1.91k | return true; |
346 | 14.1k | } |
347 | | |
348 | 0 | static int LastMoveToIndex(const SkPath& path) { return path.fLastMoveToIndex; } |
349 | | |
350 | | static bool IsRectContour(const SkPath&, bool allowPartial, int* currVerb, |
351 | | const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction, |
352 | | SkRect* rect); |
353 | | |
354 | | /** Returns true if SkPath is equivalent to nested SkRect pair when filled. |
355 | | If false, rect and dirs are unchanged. |
356 | | If true, rect and dirs are written to if not nullptr: |
357 | | setting rect[0] to outer SkRect, and rect[1] to inner SkRect; |
358 | | setting dirs[0] to SkPathDirection of outer SkRect, and dirs[1] to SkPathDirection of |
359 | | inner SkRect. |
360 | | |
361 | | @param rect storage for SkRect pair; may be nullptr |
362 | | @param dirs storage for SkPathDirection pair; may be nullptr |
363 | | @return true if SkPath contains nested SkRect pair |
364 | | */ |
365 | | static bool IsNestedFillRects(const SkPath&, SkRect rect[2], |
366 | | SkPathDirection dirs[2] = nullptr); |
367 | | |
368 | 0 | static bool IsInverseFillType(SkPathFillType fill) { |
369 | 0 | return (static_cast<int>(fill) & 2) != 0; |
370 | 0 | } |
371 | | |
372 | | /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds. |
373 | | . |
374 | | |
375 | | @param fill one of: kWinding_FillType, kEvenOdd_FillType, |
376 | | kInverseWinding_FillType, kInverseEvenOdd_FillType |
377 | | @return fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted |
378 | | */ |
379 | 0 | static SkPathFillType ConvertToNonInverseFillType(SkPathFillType fill) { |
380 | 0 | return (SkPathFillType)(static_cast<int>(fill) & 1); |
381 | 0 | } |
382 | | |
383 | | /** |
384 | | * If needed (to not blow-up under a perspective matrix), clip the path, returning the |
385 | | * answer in "result", and return true. |
386 | | * |
387 | | * Note result might be empty (if the path was completely clipped out). |
388 | | * |
389 | | * If no clipping is needed, returns false and "result" is left unchanged. |
390 | | */ |
391 | | static bool PerspectiveClip(const SkPath& src, const SkMatrix&, SkPath* result); |
392 | | |
393 | | /** |
394 | | * Gets the number of GenIDChangeListeners. If another thread has access to this path then |
395 | | * this may be stale before return and only indicates that the count was the return value |
396 | | * at some point during the execution of the function. |
397 | | */ |
398 | | static int GenIDChangeListenersCount(const SkPath&); |
399 | | |
400 | 0 | static void UpdatePathPoint(SkPath* path, int index, const SkPoint& pt) { |
401 | 0 | SkASSERT(index < path->countPoints()); |
402 | 0 | SkPathRef::Editor ed(&path->fPathRef); |
403 | 0 | ed.writablePoints()[index] = pt; |
404 | 0 | path->dirtyAfterEdit(); |
405 | 0 | } |
406 | | |
407 | 0 | static SkPathConvexity GetConvexity(const SkPath& path) { |
408 | 0 | return path.getConvexity(); |
409 | 0 | } |
410 | 0 | static SkPathConvexity GetConvexityOrUnknown(const SkPath& path) { |
411 | 0 | return path.getConvexityOrUnknown(); |
412 | 0 | } |
413 | 7.57k | static void SetConvexity(const SkPath& path, SkPathConvexity c) { |
414 | 7.57k | path.setConvexity(c); |
415 | 7.57k | } |
416 | 0 | static void ForceComputeConvexity(const SkPath& path) { |
417 | 0 | path.setConvexity(SkPathConvexity::kUnknown); |
418 | 0 | (void)path.isConvex(); |
419 | 0 | } |
420 | | |
421 | 2.32k | static void ReverseAddPath(SkPathBuilder* builder, const SkPath& reverseMe) { |
422 | 2.32k | builder->privateReverseAddPath(reverseMe); |
423 | 2.32k | } |
424 | | |
425 | | static SkPath MakePath(const SkPathVerbAnalysis& analysis, |
426 | | const SkPoint points[], |
427 | | const uint8_t verbs[], |
428 | | int verbCount, |
429 | | const SkScalar conics[], |
430 | | SkPathFillType fillType, |
431 | 1.98k | bool isVolatile) { |
432 | 1.98k | return SkPath::MakeInternal(analysis, points, verbs, verbCount, conics, fillType, |
433 | 1.98k | isVolatile); |
434 | 1.98k | } |
435 | | }; |
436 | | |
437 | | // Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics). |
438 | | // Does not return kMove or kClose. |
439 | | // Always "auto-closes" each contour. |
440 | | // Roughly the same as SkPath::Iter(path, true), but does not return moves or closes |
441 | | // |
442 | | class SkPathEdgeIter { |
443 | | const uint8_t* fVerbs; |
444 | | const uint8_t* fVerbsStop; |
445 | | const SkPoint* fPts; |
446 | | const SkPoint* fMoveToPtr; |
447 | | const SkScalar* fConicWeights; |
448 | | SkPoint fScratch[2]; // for auto-close lines |
449 | | bool fNeedsCloseLine; |
450 | | bool fNextIsNewContour; |
451 | | SkDEBUGCODE(bool fIsConic;) |
452 | | |
453 | | enum { |
454 | | kIllegalEdgeValue = 99 |
455 | | }; |
456 | | |
457 | | public: |
458 | | SkPathEdgeIter(const SkPath& path); |
459 | | |
460 | 10.3M | SkScalar conicWeight() const { |
461 | 10.3M | SkASSERT(fIsConic); |
462 | 10.3M | return *fConicWeights; |
463 | 10.3M | } |
464 | | |
465 | | enum class Edge { |
466 | | kLine = SkPath::kLine_Verb, |
467 | | kQuad = SkPath::kQuad_Verb, |
468 | | kConic = SkPath::kConic_Verb, |
469 | | kCubic = SkPath::kCubic_Verb, |
470 | | }; |
471 | | |
472 | 0 | static SkPath::Verb EdgeToVerb(Edge e) { |
473 | 0 | return SkPath::Verb(e); |
474 | 0 | } |
475 | | |
476 | | struct Result { |
477 | | const SkPoint* fPts; // points for the segment, or null if done |
478 | | Edge fEdge; |
479 | | bool fIsNewContour; |
480 | | |
481 | | // Returns true when it holds an Edge, false when the path is done. |
482 | 194M | explicit operator bool() { return fPts != nullptr; } |
483 | | }; |
484 | | |
485 | 194M | Result next() { |
486 | 194M | auto closeline = [&]() { |
487 | 45.1M | fScratch[0] = fPts[-1]; |
488 | 45.1M | fScratch[1] = *fMoveToPtr; |
489 | 45.1M | fNeedsCloseLine = false; |
490 | 45.1M | fNextIsNewContour = true; |
491 | 45.1M | return Result{ fScratch, Edge::kLine, false }; |
492 | 45.1M | }; |
493 | | |
494 | 442M | for (;;) { |
495 | 442M | SkASSERT(fVerbs <= fVerbsStop); |
496 | 442M | if (fVerbs == fVerbsStop) { |
497 | 23.3M | return fNeedsCloseLine |
498 | 23.3M | ? closeline() |
499 | 23.3M | : Result{ nullptr, Edge(kIllegalEdgeValue), false }; |
500 | 23.3M | } |
501 | | |
502 | 0 | SkDEBUGCODE(fIsConic = false;) |
503 | |
|
504 | 419M | const auto v = *fVerbs++; |
505 | 419M | switch (v) { |
506 | 274M | case SkPath::kMove_Verb: { |
507 | 274M | if (fNeedsCloseLine) { |
508 | 27.3M | auto res = closeline(); |
509 | 27.3M | fMoveToPtr = fPts++; |
510 | 27.3M | return res; |
511 | 27.3M | } |
512 | 247M | fMoveToPtr = fPts++; |
513 | 247M | fNextIsNewContour = true; |
514 | 247M | } break; |
515 | 9.33M | case SkPath::kClose_Verb: |
516 | 9.33M | if (fNeedsCloseLine) return closeline(); |
517 | 1.10M | break; |
518 | 135M | default: { |
519 | | // Actual edge. |
520 | 135M | const int pts_count = (v+2) / 2, |
521 | 135M | cws_count = (v & (v-1)) / 2; |
522 | 135M | SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1); |
523 | | |
524 | 135M | fNeedsCloseLine = true; |
525 | 135M | fPts += pts_count; |
526 | 135M | fConicWeights += cws_count; |
527 | | |
528 | 135M | SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);) |
529 | 135M | SkASSERT(fIsConic == (cws_count > 0)); |
530 | | |
531 | 135M | bool isNewContour = fNextIsNewContour; |
532 | 135M | fNextIsNewContour = false; |
533 | 135M | return { &fPts[-(pts_count + 1)], Edge(v), isNewContour }; |
534 | 9.33M | } |
535 | 419M | } |
536 | 419M | } |
537 | 194M | } Line | Count | Source | 485 | 194M | Result next() { | 486 | 194M | auto closeline = [&]() { | 487 | 194M | fScratch[0] = fPts[-1]; | 488 | 194M | fScratch[1] = *fMoveToPtr; | 489 | 194M | fNeedsCloseLine = false; | 490 | 194M | fNextIsNewContour = true; | 491 | 194M | return Result{ fScratch, Edge::kLine, false }; | 492 | 194M | }; | 493 | | | 494 | 442M | for (;;) { | 495 | 442M | SkASSERT(fVerbs <= fVerbsStop); | 496 | 442M | if (fVerbs == fVerbsStop) { | 497 | 23.3M | return fNeedsCloseLine | 498 | 23.3M | ? closeline() | 499 | 23.3M | : Result{ nullptr, Edge(kIllegalEdgeValue), false }; | 500 | 23.3M | } | 501 | | | 502 | 419M | SkDEBUGCODE(fIsConic = false;) | 503 | | | 504 | 419M | const auto v = *fVerbs++; | 505 | 419M | switch (v) { | 506 | 274M | case SkPath::kMove_Verb: { | 507 | 274M | if (fNeedsCloseLine) { | 508 | 27.3M | auto res = closeline(); | 509 | 27.3M | fMoveToPtr = fPts++; | 510 | 27.3M | return res; | 511 | 27.3M | } | 512 | 247M | fMoveToPtr = fPts++; | 513 | 247M | fNextIsNewContour = true; | 514 | 247M | } break; | 515 | 9.33M | case SkPath::kClose_Verb: | 516 | 9.33M | if (fNeedsCloseLine) return closeline(); | 517 | 1.10M | break; | 518 | 135M | default: { | 519 | | // Actual edge. | 520 | 135M | const int pts_count = (v+2) / 2, | 521 | 135M | cws_count = (v & (v-1)) / 2; | 522 | 135M | SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1); | 523 | | | 524 | 135M | fNeedsCloseLine = true; | 525 | 135M | fPts += pts_count; | 526 | 135M | fConicWeights += cws_count; | 527 | | | 528 | 135M | SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);) | 529 | 135M | SkASSERT(fIsConic == (cws_count > 0)); | 530 | | | 531 | 135M | bool isNewContour = fNextIsNewContour; | 532 | 135M | fNextIsNewContour = false; | 533 | 135M | return { &fPts[-(pts_count + 1)], Edge(v), isNewContour }; | 534 | 9.33M | } | 535 | 419M | } | 536 | 419M | } | 537 | 194M | } |
Unexecuted instantiation: SkPathEdgeIter::next() |
538 | | }; |
539 | | |
540 | | #endif |