Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkPathRef.h
Go to the documentation of this file.
1/*
2 * Copyright 2012 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 SkPathRef_DEFINED
9#define SkPathRef_DEFINED
10
11#include "include/core/SkArc.h"
13#include "include/core/SkRect.h"
21
22#include <atomic>
23#include <cstddef>
24#include <cstdint>
25#include <tuple>
26#include <utility>
27
28class SkMatrix;
29class SkRRect;
30
31// These are computed from a stream of verbs
33 bool valid;
35 unsigned segmentMask;
36};
37SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count);
38
39
40/**
41 * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
42 * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
43 * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
44 * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's
45 * constructor a pointer to a sk_sp<SkPathRef>, which may be updated to point to a new SkPathRef
46 * after the editor's constructor returns.
47 *
48 * The points and verbs are stored in a single allocation. The points are at the begining of the
49 * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points
50 * and verbs both grow into the middle of the allocation until the meet. To access verb i in the
51 * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first
52 * logical verb or the last verb in memory).
53 */
54
55class SK_API SkPathRef final : public SkNVRefCnt<SkPathRef> {
56public:
57 // See https://bugs.chromium.org/p/skia/issues/detail?id=13817 for how these sizes were
58 // determined.
62
63 enum class PathType : uint8_t {
65 kOval,
66 kRRect,
67 kArc,
68 };
69
71 unsigned segmentMask)
72 : fPoints(std::move(points))
73 , fVerbs(std::move(verbs))
74 , fConicWeights(std::move(weights))
75 {
76 fBoundsIsDirty = true; // this also invalidates fIsFinite
77 fGenerationID = 0; // recompute
78 fSegmentMask = segmentMask;
79 fType = PathType::kGeneral;
80 // The next two values don't matter unless fType is kOval or kRRect
81 fRRectOrOvalIsCCW = false;
82 fRRectOrOvalStartIdx = 0xAC;
83 fArcOval.setEmpty();
84 fArcStartAngle = fArcSweepAngle = 0.0f;
85 fArcUseCenter = false;
86 SkDEBUGCODE(fEditorsAttached.store(0);)
87
88 this->computeBounds(); // do this now, before we worry about multiple owners/threads
89 SkDEBUGCODE(this->validate();)
90 }
91
92 class Editor {
93 public:
94 Editor(sk_sp<SkPathRef>* pathRef,
95 int incReserveVerbs = 0,
96 int incReservePoints = 0,
97 int incReserveConics = 0);
98
99 ~Editor() { SkDEBUGCODE(fPathRef->fEditorsAttached--;) }
100
101 /**
102 * Returns the array of points.
103 */
104 SkPoint* writablePoints() { return fPathRef->getWritablePoints(); }
105 const SkPoint* points() const { return fPathRef->points(); }
106
107 /**
108 * Gets the ith point. Shortcut for this->points() + i
109 */
110 SkPoint* atPoint(int i) { return fPathRef->getWritablePoints() + i; }
111 const SkPoint* atPoint(int i) const { return &fPathRef->fPoints[i]; }
112
113 /**
114 * Adds the verb and allocates space for the number of points indicated by the verb. The
115 * return value is a pointer to where the points for the verb should be written.
116 * 'weight' is only used if 'verb' is kConic_Verb
117 */
118 SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) {
119 SkDEBUGCODE(fPathRef->validate();)
120 return fPathRef->growForVerb(verb, weight);
121 }
122
123 /**
124 * Allocates space for multiple instances of a particular verb and the
125 * requisite points & weights.
126 * The return pointer points at the first new point (indexed normally [<i>]).
127 * If 'verb' is kConic_Verb, 'weights' will return a pointer to the
128 * space for the conic weights (indexed normally).
129 */
130 SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb,
131 int numVbs,
132 SkScalar** weights = nullptr) {
133 return fPathRef->growForRepeatedVerb(verb, numVbs, weights);
134 }
135
136 /**
137 * Concatenates all verbs from 'path' onto the pathRef's verbs array. Increases the point
138 * count by the number of points in 'path', and the conic weight count by the number of
139 * conics in 'path'.
140 *
141 * Returns pointers to the uninitialized points and conic weights data.
142 */
143 std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path) {
144 return fPathRef->growForVerbsInPath(path);
145 }
146
147 /**
148 * Resets the path ref to a new verb and point count. The new verbs and points are
149 * uninitialized.
150 */
151 void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) {
152 fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount);
153 }
154
155 /**
156 * Gets the path ref that is wrapped in the Editor.
157 */
158 SkPathRef* pathRef() { return fPathRef; }
159
160 void setIsOval(bool isCCW, unsigned start) {
161 fPathRef->setIsOval(isCCW, start);
162 }
163
164 void setIsRRect(bool isCCW, unsigned start) {
165 fPathRef->setIsRRect(isCCW, start);
166 }
167
168 void setIsArc(const SkArc& arc) {
169 fPathRef->setIsArc(arc);
170 }
171
172 void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); }
173
174 private:
175 SkPathRef* fPathRef;
176 };
177
178 class SK_API Iter {
179 public:
180 Iter();
181 Iter(const SkPathRef&);
182
183 void setPathRef(const SkPathRef&);
184
185 /** Return the next verb in this iteration of the path. When all
186 segments have been visited, return kDone_Verb.
187
188 If any point in the path is non-finite, return kDone_Verb immediately.
189
190 @param pts The points representing the current verb and/or segment
191 This must not be NULL.
192 @return The verb for the current segment
193 */
194 uint8_t next(SkPoint pts[4]);
195 uint8_t peek() const;
196
197 SkScalar conicWeight() const { return *fConicWeights; }
198
199 private:
200 const SkPoint* fPts;
201 const uint8_t* fVerbs;
202 const uint8_t* fVerbStop;
203 const SkScalar* fConicWeights;
204 };
205
206public:
207 /**
208 * Gets a path ref with no verbs or points.
209 */
210 static SkPathRef* CreateEmpty();
211
212 /**
213 * Returns true if all of the points in this path are finite, meaning there
214 * are no infinities and no NaNs.
215 */
216 bool isFinite() const {
217 if (fBoundsIsDirty) {
218 this->computeBounds();
219 }
220 return SkToBool(fIsFinite);
221 }
222
223 /**
224 * Returns a mask, where each bit corresponding to a SegmentMask is
225 * set if the path contains 1 or more segments of that type.
226 * Returns 0 for an empty path (no segments).
227 */
228 uint32_t getSegmentMasks() const { return fSegmentMask; }
229
230 /** Returns true if the path is an oval.
231 *
232 * @param rect returns the bounding rect of this oval. It's a circle
233 * if the height and width are the same.
234 * @param isCCW is the oval CCW (or CW if false).
235 * @param start indicates where the contour starts on the oval (see
236 * SkPath::addOval for intepretation of the index).
237 *
238 * @return true if this path is an oval.
239 * Tracking whether a path is an oval is considered an
240 * optimization for performance and so some paths that are in
241 * fact ovals can report false.
242 */
243 bool isOval(SkRect* rect, bool* isCCW, unsigned* start) const {
244 if (fType == PathType::kOval) {
245 if (rect) {
246 *rect = this->getBounds();
247 }
248 if (isCCW) {
249 *isCCW = SkToBool(fRRectOrOvalIsCCW);
250 }
251 if (start) {
252 *start = fRRectOrOvalStartIdx;
253 }
254 }
255
256 return fType == PathType::kOval;
257 }
258
259 bool isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const;
260
261 bool isArc(SkArc* arc) const {
262 if (fType == PathType::kArc) {
263 if (arc) {
264 arc->fOval = fArcOval;
265 arc->fStartAngle = fArcStartAngle;
266 arc->fSweepAngle = fArcSweepAngle;
267 arc->fUseCenter = fArcUseCenter;
268 }
269 }
270
271 return fType == PathType::kArc;
272 }
273
274 bool hasComputedBounds() const {
275 return !fBoundsIsDirty;
276 }
277
278 /** Returns the bounds of the path's points. If the path contains 0 or 1
279 points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
280 Note: this bounds may be larger than the actual shape, since curves
281 do not extend as far as their control points.
282 */
283 const SkRect& getBounds() const {
284 if (fBoundsIsDirty) {
285 this->computeBounds();
286 }
287 return fBounds;
288 }
289
290 SkRRect getRRect() const;
291
292 /**
293 * Transforms a path ref by a matrix, allocating a new one only if necessary.
294 */
295 static void CreateTransformedCopy(sk_sp<SkPathRef>* dst,
296 const SkPathRef& src,
297 const SkMatrix& matrix);
298
299 // static SkPathRef* CreateFromBuffer(SkRBuffer* buffer);
300
301 /**
302 * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
303 * repopulated with approximately the same number of verbs and points. A new path ref is created
304 * only if necessary.
305 */
306 static void Rewind(sk_sp<SkPathRef>* pathRef);
307
308 ~SkPathRef();
309 int countPoints() const { return fPoints.size(); }
310 int countVerbs() const { return fVerbs.size(); }
311 int countWeights() const { return fConicWeights.size(); }
312
313 size_t approximateBytesUsed() const;
314
315 /**
316 * Returns a pointer one beyond the first logical verb (last verb in memory order).
317 */
318 const uint8_t* verbsBegin() const { return fVerbs.begin(); }
319
320 /**
321 * Returns a const pointer to the first verb in memory (which is the last logical verb).
322 */
323 const uint8_t* verbsEnd() const { return fVerbs.end(); }
324
325 /**
326 * Returns a const pointer to the first point.
327 */
328 const SkPoint* points() const { return fPoints.begin(); }
329
330 /**
331 * Shortcut for this->points() + this->countPoints()
332 */
333 const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
334
335 const SkScalar* conicWeights() const { return fConicWeights.begin(); }
336 const SkScalar* conicWeightsEnd() const { return fConicWeights.end(); }
337
338 /**
339 * Convenience methods for getting to a verb or point by index.
340 */
341 uint8_t atVerb(int index) const { return fVerbs[index]; }
342 const SkPoint& atPoint(int index) const { return fPoints[index]; }
343
344 bool operator== (const SkPathRef& ref) const;
345
346 void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const;
347
348 /**
349 * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
350 * same ID then they have the same verbs and points. However, two path refs may have the same
351 * contents but different genIDs.
352 * skbug.com/1762 for background on why fillType is necessary (for now).
353 */
354 uint32_t genID(uint8_t fillType) const;
355
356 void addGenIDChangeListener(sk_sp<SkIDChangeListener>); // Threadsafe.
357 int genIDChangeListenerCount(); // Threadsafe
358
359 bool dataMatchesVerbs() const;
360 bool isValid() const;
361 SkDEBUGCODE(void validate() const { SkASSERT(this->isValid()); } )
362
363 /**
364 * Resets this SkPathRef to a clean state.
365 */
366 void reset();
367
369 return fGenerationID == kEmptyGenID;
370 }
371
372private:
374 kLegacyRRectOrOvalStartIdx_SerializationShift = 28, // requires 3 bits, ignored.
375 kLegacyRRectOrOvalIsCCW_SerializationShift = 27, // requires 1 bit, ignored.
376 kLegacyIsRRect_SerializationShift = 26, // requires 1 bit, ignored.
377 kIsFinite_SerializationShift = 25, // requires 1 bit
378 kLegacyIsOval_SerializationShift = 24, // requires 1 bit, ignored.
379 kSegmentMask_SerializationShift = 0 // requires 4 bits (deprecated)
380 };
381
382 SkPathRef(int numVerbs = 0, int numPoints = 0, int numConics = 0) {
383 fBoundsIsDirty = true; // this also invalidates fIsFinite
384 fGenerationID = kEmptyGenID;
385 fSegmentMask = 0;
386 fType = PathType::kGeneral;
387 // The next two values don't matter unless fType is kOval or kRRect
388 fRRectOrOvalIsCCW = false;
389 fRRectOrOvalStartIdx = 0xAC;
390 fArcOval.setEmpty();
391 fArcStartAngle = fArcSweepAngle = 0.0f;
392 fArcUseCenter = false;
393 if (numPoints > 0) {
394 fPoints.reserve_exact(numPoints);
395 }
396 if (numVerbs > 0) {
397 fVerbs.reserve_exact(numVerbs);
398 }
399 if (numConics > 0) {
400 fConicWeights.reserve_exact(numConics);
401 }
402 SkDEBUGCODE(fEditorsAttached.store(0);)
403 SkDEBUGCODE(this->validate();)
404 }
405
406 void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints, int additionalReserveConics);
407
408 // Return true if the computed bounds are finite.
409 static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) {
410 return bounds->setBoundsCheck(ref.points(), ref.countPoints());
411 }
412
413 // called, if dirty, by getBounds()
414 void computeBounds() const {
415 SkDEBUGCODE(this->validate();)
416 // TODO: remove fBoundsIsDirty and fIsFinite,
417 // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite.
418 SkASSERT(fBoundsIsDirty);
419
420 fIsFinite = ComputePtBounds(&fBounds, *this);
421 fBoundsIsDirty = false;
422 }
423
424 void setBounds(const SkRect& rect) {
425 SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom);
426 fBounds = rect;
427 fBoundsIsDirty = false;
428 fIsFinite = fBounds.isFinite();
429 }
430
431 /** Makes additional room but does not change the counts or change the genID */
432 void incReserve(int additionalVerbs, int additionalPoints, int additionalConics) {
433 SkDEBUGCODE(this->validate();)
434 // Use reserve() so that if there is not enough space, the array will grow with some
435 // additional space. This ensures repeated calls to grow won't always allocate.
436 if (additionalPoints > 0) {
437 fPoints.reserve(fPoints.size() + additionalPoints);
438 }
439 if (additionalVerbs > 0) {
440 fVerbs.reserve(fVerbs.size() + additionalVerbs);
441 }
442 if (additionalConics > 0) {
443 fConicWeights.reserve(fConicWeights.size() + additionalConics);
444 }
445 SkDEBUGCODE(this->validate();)
446 }
447
448 /**
449 * Resets all state except that of the verbs, points, and conic-weights.
450 * Intended to be called from other functions that reset state.
451 */
452 void commonReset() {
453 SkDEBUGCODE(this->validate();)
454 this->callGenIDChangeListeners();
455 fBoundsIsDirty = true; // this also invalidates fIsFinite
456 fGenerationID = 0;
457
458 fSegmentMask = 0;
459 fType = PathType::kGeneral;
460 }
461
462 /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also
463 * allocates space for reserveVerb additional verbs and reservePoints additional points.*/
464 void resetToSize(int verbCount, int pointCount, int conicCount,
465 int reserveVerbs = 0, int reservePoints = 0,
466 int reserveConics = 0) {
467 this->commonReset();
468 // Use reserve_exact() so the arrays are sized to exactly fit the data.
469 fPoints.reserve_exact(pointCount + reservePoints);
470 fPoints.resize_back(pointCount);
471
472 fVerbs.reserve_exact(verbCount + reserveVerbs);
473 fVerbs.resize_back(verbCount);
474
475 fConicWeights.reserve_exact(conicCount + reserveConics);
476 fConicWeights.resize_back(conicCount);
477 SkDEBUGCODE(this->validate();)
478 }
479
480 /**
481 * Increases the verb count by numVbs and point count by the required amount.
482 * The new points are uninitialized. All the new verbs are set to the specified
483 * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the
484 * uninitialized conic weights.
485 */
486 SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights);
487
488 /**
489 * Increases the verb count 1, records the new verb, and creates room for the requisite number
490 * of additional points. A pointer to the first point is returned. Any new points are
491 * uninitialized.
492 */
493 SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
494
495 /**
496 * Concatenates all verbs from 'path' onto our own verbs array. Increases the point count by the
497 * number of points in 'path', and the conic weight count by the number of conics in 'path'.
498 *
499 * Returns pointers to the uninitialized points and conic weights data.
500 */
501 std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path);
502
503 /**
504 * Private, non-const-ptr version of the public function verbsMemBegin().
505 */
506 uint8_t* verbsBeginWritable() { return fVerbs.begin(); }
507
508 /**
509 * Called the first time someone calls CreateEmpty to actually create the singleton.
510 */
512
513 void setIsOval(bool isCCW, unsigned start) {
514 fType = PathType::kOval;
515 fRRectOrOvalIsCCW = isCCW;
516 fRRectOrOvalStartIdx = SkToU8(start);
517 }
518
519 void setIsRRect(bool isCCW, unsigned start) {
520 fType = PathType::kRRect;
521 fRRectOrOvalIsCCW = isCCW;
522 fRRectOrOvalStartIdx = SkToU8(start);
523 }
524
525 void setIsArc(const SkArc& arc) {
526 fType = PathType::kArc;
527 fArcOval = arc.fOval;
528 fArcStartAngle = arc.fStartAngle;
529 fArcSweepAngle = arc.fSweepAngle;
530 fArcUseCenter = arc.fUseCenter;
531 }
532
533 // called only by the editor. Note that this is not a const function.
534 SkPoint* getWritablePoints() {
535 SkDEBUGCODE(this->validate();)
536 fType = PathType::kGeneral;
537 return fPoints.begin();
538 }
539
540 const SkPoint* getPoints() const {
541 SkDEBUGCODE(this->validate();)
542 return fPoints.begin();
543 }
544
545 void callGenIDChangeListeners();
546
547 mutable SkRect fBounds;
548
549 enum {
550 kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
551 };
552 mutable uint32_t fGenerationID;
553 SkIDChangeListener::List fGenIDChangeListeners;
554
555 PointsArray fPoints;
556 VerbsArray fVerbs;
557 ConicWeightsArray fConicWeights;
558
559 SkDEBUGCODE(std::atomic<int> fEditorsAttached;) // assert only one editor in use at any time.
560
561 mutable uint8_t fBoundsIsDirty;
562 mutable bool fIsFinite; // only meaningful if bounds are valid
563
564 PathType fType;
565 // Both the circle and rrect special cases have a notion of direction and starting point
566 // The next two variables store that information for either.
567 bool fRRectOrOvalIsCCW;
568 uint8_t fRRectOrOvalStartIdx;
569 uint8_t fSegmentMask;
570 // If the path is an arc, these four variables store that information.
571 // We should just store an SkArc, but alignment would cost us 8 more bytes.
572 bool fArcUseCenter;
573 SkRect fArcOval;
574 SkScalar fArcStartAngle;
575 SkScalar fArcSweepAngle;
576
578 friend class ForceIsRRect_Private; // unit test isRRect
579 friend class SkPath;
580 friend class SkPathBuilder;
581 friend class SkPathPriv;
582};
583
584#endif
SkPoint fPts[2]
m reset()
int count
static const int points[]
const SkRect fBounds
static float next(float f)
#define SK_API
Definition SkAPI.h:35
#define SkASSERT(cond)
Definition SkAssert.h:116
static double interpolate(double A, double B, double t)
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
SkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t verbs[], int count)
Definition SkPath.cpp:3447
@ kGeneral
@ kRRect
SerializationOffsets
bool operator==(const sk_sp< T > &a, const sk_sp< U > &b)
Definition SkRefCnt.h:345
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
constexpr uint8_t SkToU8(S x)
Definition SkTo.h:22
void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount)
Definition SkPathRef.h:151
std::tuple< SkPoint *, SkScalar * > growForVerbsInPath(const SkPathRef &path)
Definition SkPathRef.h:143
SkPoint * growForRepeatedVerb(int verb, int numVbs, SkScalar **weights=nullptr)
Definition SkPathRef.h:130
void setIsOval(bool isCCW, unsigned start)
Definition SkPathRef.h:160
SkPoint * atPoint(int i)
Definition SkPathRef.h:110
const SkPoint * points() const
Definition SkPathRef.h:105
SkPoint * growForVerb(int verb, SkScalar weight=0)
Definition SkPathRef.h:118
void setIsRRect(bool isCCW, unsigned start)
Definition SkPathRef.h:164
SkPathRef * pathRef()
Definition SkPathRef.h:158
const SkPoint * atPoint(int i) const
Definition SkPathRef.h:111
void setBounds(const SkRect &rect)
Definition SkPathRef.h:172
SkPoint * writablePoints()
Definition SkPathRef.h:104
void setIsArc(const SkArc &arc)
Definition SkPathRef.h:168
SkScalar conicWeight() const
Definition SkPathRef.h:197
SkDEBUGCODE(void validate() const { SkASSERT(this->isValid());}) void reset()
friend SkPathRef * sk_create_empty_pathref()
const SkPoint & atPoint(int index) const
Definition SkPathRef.h:342
const SkRect & getBounds() const
Definition SkPathRef.h:283
bool isFinite() const
Definition SkPathRef.h:216
bool isInitialEmptyPathRef() const
Definition SkPathRef.h:368
int countWeights() const
Definition SkPathRef.h:311
const uint8_t * verbsEnd() const
Definition SkPathRef.h:323
const SkPoint * pointsEnd() const
Definition SkPathRef.h:333
SkPathRef(PointsArray points, VerbsArray verbs, ConicWeightsArray weights, unsigned segmentMask)
Definition SkPathRef.h:70
const SkScalar * conicWeightsEnd() const
Definition SkPathRef.h:336
uint8_t atVerb(int index) const
Definition SkPathRef.h:341
const SkPoint * points() const
Definition SkPathRef.h:328
bool hasComputedBounds() const
Definition SkPathRef.h:274
const SkScalar * conicWeights() const
Definition SkPathRef.h:335
int countPoints() const
Definition SkPathRef.h:309
const uint8_t * verbsBegin() const
Definition SkPathRef.h:318
bool isArc(SkArc *arc) const
Definition SkPathRef.h:261
int countVerbs() const
Definition SkPathRef.h:310
bool isOval(SkRect *rect, bool *isCCW, unsigned *start) const
Definition SkPathRef.h:243
uint32_t getSegmentMasks() const
Definition SkPathRef.h:228
float SkScalar
Definition extension.cpp:12
Optional< SkRect > bounds
Definition SkRecords.h:189
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
Definition copy.py:1
Definition ref_ptr.h:256
Definition SkArc.h:15
bool fUseCenter
Definition SkArc.h:26
SkScalar fSweepAngle
Definition SkArc.h:22
SkScalar fStartAngle
Definition SkArc.h:20
SkRect fOval
Definition SkArc.h:17
unsigned segmentMask
Definition SkPathRef.h:35
bool isFinite() const
Definition SkRect.h:711