1 /*
2 * Copyright 2006 The Android Open Source Project
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 #include "include/core/SkPath.h"
9
10 #include "include/core/SkData.h"
11 #include "include/core/SkMath.h"
12 #include "include/core/SkRRect.h"
13 #include "include/private/SkMacros.h"
14 #include "include/private/SkPathRef.h"
15 #include "include/private/SkTo.h"
16 #include "src/core/SkBuffer.h"
17 #include "src/core/SkCubicClipper.h"
18 #include "src/core/SkGeometry.h"
19 #include "src/core/SkMatrixPriv.h"
20 #include "src/core/SkPathPriv.h"
21 #include "src/core/SkPointPriv.h"
22 #include "src/core/SkSafeMath.h"
23 #include "src/core/SkTLazy.h"
24 // need SkDVector
25 #include "src/pathops/SkPathOpsPoint.h"
26
27 #include <cmath>
28 #include <utility>
29
30 struct SkPath_Storage_Equivalent {
31 void* fPtr;
32 int32_t fIndex;
33 uint32_t fFlags;
34 };
35
36 static_assert(sizeof(SkPath) == sizeof(SkPath_Storage_Equivalent),
37 "Please keep an eye on SkPath packing.");
38
poly_eval(float A,float B,float C,float t)39 static float poly_eval(float A, float B, float C, float t) {
40 return (A * t + B) * t + C;
41 }
42
poly_eval(float A,float B,float C,float D,float t)43 static float poly_eval(float A, float B, float C, float D, float t) {
44 return ((A * t + B) * t + C) * t + D;
45 }
46
47 ////////////////////////////////////////////////////////////////////////////
48
49 /**
50 * Path.bounds is defined to be the bounds of all the control points.
51 * If we called bounds.join(r) we would skip r if r was empty, which breaks
52 * our promise. Hence we have a custom joiner that doesn't look at emptiness
53 */
joinNoEmptyChecks(SkRect * dst,const SkRect & src)54 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
55 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
56 dst->fTop = SkMinScalar(dst->fTop, src.fTop);
57 dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
58 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
59 }
60
is_degenerate(const SkPath & path)61 static bool is_degenerate(const SkPath& path) {
62 SkPath::Iter iter(path, false);
63 SkPoint pts[4];
64 return SkPath::kDone_Verb == iter.next(pts);
65 }
66
67 class SkAutoDisableDirectionCheck {
68 public:
SkAutoDisableDirectionCheck(SkPath * path)69 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
70 fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->getFirstDirection());
71 }
72
~SkAutoDisableDirectionCheck()73 ~SkAutoDisableDirectionCheck() {
74 fPath->setFirstDirection(fSaved);
75 }
76
77 private:
78 SkPath* fPath;
79 SkPathPriv::FirstDirection fSaved;
80 };
81 #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
82
83 /* This guy's constructor/destructor bracket a path editing operation. It is
84 used when we know the bounds of the amount we are going to add to the path
85 (usually a new contour, but not required).
86
87 It captures some state about the path up front (i.e. if it already has a
88 cached bounds), and then if it can, it updates the cache bounds explicitly,
89 avoiding the need to revisit all of the points in getBounds().
90
91 It also notes if the path was originally degenerate, and if so, sets
92 isConvex to true. Thus it can only be used if the contour being added is
93 convex.
94 */
95 class SkAutoPathBoundsUpdate {
96 public:
SkAutoPathBoundsUpdate(SkPath * path,const SkRect & r)97 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
98 this->init(path);
99 }
100
SkAutoPathBoundsUpdate(SkPath * path,SkScalar left,SkScalar top,SkScalar right,SkScalar bottom)101 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
102 SkScalar right, SkScalar bottom) {
103 fRect.set(left, top, right, bottom);
104 this->init(path);
105 }
106
~SkAutoPathBoundsUpdate()107 ~SkAutoPathBoundsUpdate() {
108 fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
109 : SkPath::kUnknown_Convexity);
110 if ((fEmpty || fHasValidBounds) && fRect.isFinite()) {
111 fPath->setBounds(fRect);
112 }
113 }
114
115 private:
116 SkPath* fPath;
117 SkRect fRect;
118 bool fHasValidBounds;
119 bool fDegenerate;
120 bool fEmpty;
121
init(SkPath * path)122 void init(SkPath* path) {
123 // Cannot use fRect for our bounds unless we know it is sorted
124 fRect.sort();
125 fPath = path;
126 // Mark the path's bounds as dirty if (1) they are, or (2) the path
127 // is non-finite, and therefore its bounds are not meaningful
128 fHasValidBounds = path->hasComputedBounds() && path->isFinite();
129 fEmpty = path->isEmpty();
130 if (fHasValidBounds && !fEmpty) {
131 joinNoEmptyChecks(&fRect, fPath->getBounds());
132 }
133 fDegenerate = is_degenerate(*path);
134 }
135 };
136 #define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
137
138 ////////////////////////////////////////////////////////////////////////////
139
140 /*
141 Stores the verbs and points as they are given to us, with exceptions:
142 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
143 - we insert a Move(0,0) if Line | Quad | Cubic is our first command
144
145 The iterator does more cleanup, especially if forceClose == true
146 1. If we encounter degenerate segments, remove them
147 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
148 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
149 4. if we encounter Line | Quad | Cubic after Close, cons up a Move
150 */
151
152 ////////////////////////////////////////////////////////////////////////////
153
154 // flag to require a moveTo if we begin with something else, like lineTo etc.
155 #define INITIAL_LASTMOVETOINDEX_VALUE ~0
156
SkPath()157 SkPath::SkPath()
158 : fPathRef(SkPathRef::CreateEmpty()) {
159 this->resetFields();
160 fIsVolatile = false;
161 }
162
resetFields()163 void SkPath::resetFields() {
164 //fPathRef is assumed to have been emptied by the caller.
165 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
166 fFillType = kWinding_FillType;
167 this->setConvexity(kUnknown_Convexity);
168 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
169
170 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we
171 // don't want to muck with it if it's been set to something non-nullptr.
172 }
173
SkPath(const SkPath & that)174 SkPath::SkPath(const SkPath& that)
175 : fPathRef(SkRef(that.fPathRef.get())) {
176 this->copyFields(that);
177 SkDEBUGCODE(that.validate();)
178 }
179
~SkPath()180 SkPath::~SkPath() {
181 SkDEBUGCODE(this->validate();)
182 }
183
operator =(const SkPath & that)184 SkPath& SkPath::operator=(const SkPath& that) {
185 SkDEBUGCODE(that.validate();)
186
187 if (this != &that) {
188 fPathRef.reset(SkRef(that.fPathRef.get()));
189 this->copyFields(that);
190 }
191 SkDEBUGCODE(this->validate();)
192 return *this;
193 }
194
copyFields(const SkPath & that)195 void SkPath::copyFields(const SkPath& that) {
196 //fPathRef is assumed to have been set by the caller.
197 fLastMoveToIndex = that.fLastMoveToIndex;
198 fFillType = that.fFillType;
199 fIsVolatile = that.fIsVolatile;
200
201 // Non-atomic assignment of atomic values.
202 this->setConvexity(that.getConvexityOrUnknown());
203 this->setFirstDirection(that.getFirstDirection());
204 }
205
operator ==(const SkPath & a,const SkPath & b)206 bool operator==(const SkPath& a, const SkPath& b) {
207 // note: don't need to look at isConvex or bounds, since just comparing the
208 // raw data is sufficient.
209 return &a == &b ||
210 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
211 }
212
swap(SkPath & that)213 void SkPath::swap(SkPath& that) {
214 if (this != &that) {
215 fPathRef.swap(that.fPathRef);
216 std::swap(fLastMoveToIndex, that.fLastMoveToIndex);
217
218 const auto ft = fFillType;
219 fFillType = that.fFillType;
220 that.fFillType = ft;
221
222 const auto iv = fIsVolatile;
223 fIsVolatile = that.fIsVolatile;
224 that.fIsVolatile = iv;
225
226 // Non-atomic swaps of atomic values.
227 Convexity c = this->getConvexityOrUnknown();
228 this->setConvexity(that.getConvexityOrUnknown());
229 that.setConvexity(c);
230
231 uint8_t fd = this->getFirstDirection();
232 this->setFirstDirection(that.getFirstDirection());
233 that.setFirstDirection(fd);
234 }
235 }
236
isInterpolatable(const SkPath & compare) const237 bool SkPath::isInterpolatable(const SkPath& compare) const {
238 int count = fPathRef->countVerbs();
239 if (count != compare.fPathRef->countVerbs()) {
240 return false;
241 }
242 if (!count) {
243 return true;
244 }
245 if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
246 count)) {
247 return false;
248 }
249 return !fPathRef->countWeights() ||
250 !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
251 fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
252 }
253
interpolate(const SkPath & ending,SkScalar weight,SkPath * out) const254 bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
255 int pointCount = fPathRef->countPoints();
256 if (pointCount != ending.fPathRef->countPoints()) {
257 return false;
258 }
259 if (!pointCount) {
260 return true;
261 }
262 out->reset();
263 out->addPath(*this);
264 fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get());
265 return true;
266 }
267
check_edge_against_rect(const SkPoint & p0,const SkPoint & p1,const SkRect & rect,SkPathPriv::FirstDirection dir)268 static inline bool check_edge_against_rect(const SkPoint& p0,
269 const SkPoint& p1,
270 const SkRect& rect,
271 SkPathPriv::FirstDirection dir) {
272 const SkPoint* edgeBegin;
273 SkVector v;
274 if (SkPathPriv::kCW_FirstDirection == dir) {
275 v = p1 - p0;
276 edgeBegin = &p0;
277 } else {
278 v = p0 - p1;
279 edgeBegin = &p1;
280 }
281 if (v.fX || v.fY) {
282 // check the cross product of v with the vec from edgeBegin to each rect corner
283 SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX);
284 SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY);
285 SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX);
286 SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY);
287 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
288 return false;
289 }
290 }
291 return true;
292 }
293
conservativelyContainsRect(const SkRect & rect) const294 bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
295 // This only handles non-degenerate convex paths currently.
296 if (kConvex_Convexity != this->getConvexity()) {
297 return false;
298 }
299
300 SkPathPriv::FirstDirection direction;
301 if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
302 return false;
303 }
304
305 SkPoint firstPt;
306 SkPoint prevPt;
307 SkPath::Iter iter(*this, true);
308 SkPath::Verb verb;
309 SkPoint pts[4];
310 int segmentCount = 0;
311 SkDEBUGCODE(int moveCnt = 0;)
312 SkDEBUGCODE(int closeCount = 0;)
313
314 while ((verb = iter.next(pts)) != kDone_Verb) {
315 int nextPt = -1;
316 switch (verb) {
317 case kMove_Verb:
318 SkASSERT(!segmentCount && !closeCount);
319 SkDEBUGCODE(++moveCnt);
320 firstPt = prevPt = pts[0];
321 break;
322 case kLine_Verb:
323 if (!SkPathPriv::AllPointsEq(pts, 2)) {
324 nextPt = 1;
325 SkASSERT(moveCnt && !closeCount);
326 ++segmentCount;
327 }
328 break;
329 case kQuad_Verb:
330 case kConic_Verb:
331 if (!SkPathPriv::AllPointsEq(pts, 3)) {
332 SkASSERT(moveCnt && !closeCount);
333 ++segmentCount;
334 nextPt = 2;
335 }
336 break;
337 case kCubic_Verb:
338 if (!SkPathPriv::AllPointsEq(pts, 4)) {
339 SkASSERT(moveCnt && !closeCount);
340 ++segmentCount;
341 nextPt = 3;
342 }
343 break;
344 case kClose_Verb:
345 SkDEBUGCODE(++closeCount;)
346 break;
347 default:
348 SkDEBUGFAIL("unknown verb");
349 }
350 if (-1 != nextPt) {
351 if (SkPath::kConic_Verb == verb) {
352 SkConic orig;
353 orig.set(pts, iter.conicWeight());
354 SkPoint quadPts[5];
355 int count = orig.chopIntoQuadsPOW2(quadPts, 1);
356 SkASSERT_RELEASE(2 == count);
357
358 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
359 return false;
360 }
361 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
362 return false;
363 }
364 } else {
365 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
366 return false;
367 }
368 }
369 prevPt = pts[nextPt];
370 }
371 }
372
373 if (segmentCount) {
374 return check_edge_against_rect(prevPt, firstPt, rect, direction);
375 }
376 return false;
377 }
378
getGenerationID() const379 uint32_t SkPath::getGenerationID() const {
380 uint32_t genID = fPathRef->genID();
381 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
382 SkASSERT((unsigned)fFillType < (1 << (32 - SkPathPriv::kPathRefGenIDBitCnt)));
383 genID |= static_cast<uint32_t>(fFillType) << SkPathPriv::kPathRefGenIDBitCnt;
384 #endif
385 return genID;
386 }
387
reset()388 SkPath& SkPath::reset() {
389 SkDEBUGCODE(this->validate();)
390
391 fPathRef.reset(SkPathRef::CreateEmpty());
392 this->resetFields();
393 return *this;
394 }
395
rewind()396 SkPath& SkPath::rewind() {
397 SkDEBUGCODE(this->validate();)
398
399 SkPathRef::Rewind(&fPathRef);
400 this->resetFields();
401 return *this;
402 }
403
isLastContourClosed() const404 bool SkPath::isLastContourClosed() const {
405 int verbCount = fPathRef->countVerbs();
406 if (0 == verbCount) {
407 return false;
408 }
409 return kClose_Verb == fPathRef->atVerb(verbCount - 1);
410 }
411
isLine(SkPoint line[2]) const412 bool SkPath::isLine(SkPoint line[2]) const {
413 int verbCount = fPathRef->countVerbs();
414
415 if (2 == verbCount) {
416 SkASSERT(kMove_Verb == fPathRef->atVerb(0));
417 if (kLine_Verb == fPathRef->atVerb(1)) {
418 SkASSERT(2 == fPathRef->countPoints());
419 if (line) {
420 const SkPoint* pts = fPathRef->points();
421 line[0] = pts[0];
422 line[1] = pts[1];
423 }
424 return true;
425 }
426 }
427 return false;
428 }
429
430 /*
431 Determines if path is a rect by keeping track of changes in direction
432 and looking for a loop either clockwise or counterclockwise.
433
434 The direction is computed such that:
435 0: vertical up
436 1: horizontal left
437 2: vertical down
438 3: horizontal right
439
440 A rectangle cycles up/right/down/left or up/left/down/right.
441
442 The test fails if:
443 The path is closed, and followed by a line.
444 A second move creates a new endpoint.
445 A diagonal line is parsed.
446 There's more than four changes of direction.
447 There's a discontinuity on the line (e.g., a move in the middle)
448 The line reverses direction.
449 The path contains a quadratic or cubic.
450 The path contains fewer than four points.
451 *The rectangle doesn't complete a cycle.
452 *The final point isn't equal to the first point.
453
454 *These last two conditions we relax if we have a 3-edge path that would
455 form a rectangle if it were closed (as we do when we fill a path)
456
457 It's OK if the path has:
458 Several colinear line segments composing a rectangle side.
459 Single points on the rectangle side.
460
461 The direction takes advantage of the corners found since opposite sides
462 must travel in opposite directions.
463
464 FIXME: Allow colinear quads and cubics to be treated like lines.
465 FIXME: If the API passes fill-only, return true if the filled stroke
466 is a rectangle, though the caller failed to close the path.
467
468 directions values:
469 0x1 is set if the segment is horizontal
470 0x2 is set if the segment is moving to the right or down
471 thus:
472 two directions are opposites iff (dirA ^ dirB) == 0x2
473 two directions are perpendicular iff (dirA ^ dirB) == 0x1
474
475 */
rect_make_dir(SkScalar dx,SkScalar dy)476 static int rect_make_dir(SkScalar dx, SkScalar dy) {
477 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
478 }
479
isRectContour(bool allowPartial,int * currVerb,const SkPoint ** ptsPtr,bool * isClosed,Direction * direction,SkRect * rect) const480 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
481 bool* isClosed, Direction* direction, SkRect* rect) const {
482 int corners = 0;
483 SkPoint closeXY; // used to determine if final line falls on a diagonal
484 SkPoint lineStart; // used to construct line from previous point
485 const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves)
486 const SkPoint* lastPt = nullptr; // last point in the rect (last of lines or first if closed)
487 SkPoint firstCorner;
488 SkPoint thirdCorner;
489 const SkPoint* pts = *ptsPtr;
490 const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects
491 lineStart.set(0, 0);
492 signed char directions[] = {-1, -1, -1, -1, -1}; // -1 to 3; -1 is uninitialized
493 bool closedOrMoved = false;
494 bool autoClose = false;
495 bool insertClose = false;
496 int verbCnt = fPathRef->countVerbs();
497 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
498 uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
499 switch (verb) {
500 case kClose_Verb:
501 savePts = pts;
502 autoClose = true;
503 insertClose = false;
504 case kLine_Verb: {
505 if (kClose_Verb != verb) {
506 lastPt = pts;
507 }
508 SkPoint lineEnd = kClose_Verb == verb ? *firstPt : *pts++;
509 SkVector lineDelta = lineEnd - lineStart;
510 if (lineDelta.fX && lineDelta.fY) {
511 return false; // diagonal
512 }
513 if (!lineDelta.isFinite()) {
514 return false; // path contains infinity or NaN
515 }
516 if (lineStart == lineEnd) {
517 break; // single point on side OK
518 }
519 int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3
520 if (0 == corners) {
521 directions[0] = nextDirection;
522 corners = 1;
523 closedOrMoved = false;
524 lineStart = lineEnd;
525 break;
526 }
527 if (closedOrMoved) {
528 return false; // closed followed by a line
529 }
530 if (autoClose && nextDirection == directions[0]) {
531 break; // colinear with first
532 }
533 closedOrMoved = autoClose;
534 if (directions[corners - 1] == nextDirection) {
535 if (3 == corners && kLine_Verb == verb) {
536 thirdCorner = lineEnd;
537 }
538 lineStart = lineEnd;
539 break; // colinear segment
540 }
541 directions[corners++] = nextDirection;
542 // opposite lines must point in opposite directions; xoring them should equal 2
543 switch (corners) {
544 case 2:
545 firstCorner = lineStart;
546 break;
547 case 3:
548 if ((directions[0] ^ directions[2]) != 2) {
549 return false;
550 }
551 thirdCorner = lineEnd;
552 break;
553 case 4:
554 if ((directions[1] ^ directions[3]) != 2) {
555 return false;
556 }
557 break;
558 default:
559 return false; // too many direction changes
560 }
561 lineStart = lineEnd;
562 break;
563 }
564 case kQuad_Verb:
565 case kConic_Verb:
566 case kCubic_Verb:
567 return false; // quadratic, cubic not allowed
568 case kMove_Verb:
569 if (allowPartial && !autoClose && directions[0] >= 0) {
570 insertClose = true;
571 *currVerb -= 1; // try move again afterwards
572 goto addMissingClose;
573 }
574 if (!corners) {
575 firstPt = pts;
576 } else {
577 closeXY = *firstPt - *lastPt;
578 if (closeXY.fX && closeXY.fY) {
579 return false; // we're diagonal, abort
580 }
581 }
582 lineStart = *pts++;
583 closedOrMoved = true;
584 break;
585 default:
586 SkDEBUGFAIL("unexpected verb");
587 break;
588 }
589 *currVerb += 1;
590 addMissingClose:
591 ;
592 }
593 // Success if 4 corners and first point equals last
594 if (corners < 3 || corners > 4) {
595 return false;
596 }
597 if (savePts) {
598 *ptsPtr = savePts;
599 }
600 // check if close generates diagonal
601 closeXY = *firstPt - *lastPt;
602 if (closeXY.fX && closeXY.fY) {
603 return false;
604 }
605 if (rect) {
606 rect->set(firstCorner, thirdCorner);
607 }
608 if (isClosed) {
609 *isClosed = autoClose;
610 }
611 if (direction) {
612 *direction = directions[0] == ((directions[1] + 1) & 3) ? kCW_Direction : kCCW_Direction;
613 }
614 return true;
615 }
616
isRect(SkRect * rect,bool * isClosed,Direction * direction) const617 bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
618 SkDEBUGCODE(this->validate();)
619 int currVerb = 0;
620 const SkPoint* pts = fPathRef->points();
621 return this->isRectContour(false, &currVerb, &pts, isClosed, direction, rect);
622 }
623
isNestedFillRects(SkRect rects[2],Direction dirs[2]) const624 bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
625 SkDEBUGCODE(this->validate();)
626 int currVerb = 0;
627 const SkPoint* pts = fPathRef->points();
628 Direction testDirs[2];
629 SkRect testRects[2];
630 if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) {
631 return false;
632 }
633 if (isRectContour(false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) {
634 if (testRects[0].contains(testRects[1])) {
635 if (rects) {
636 rects[0] = testRects[0];
637 rects[1] = testRects[1];
638 }
639 if (dirs) {
640 dirs[0] = testDirs[0];
641 dirs[1] = testDirs[1];
642 }
643 return true;
644 }
645 if (testRects[1].contains(testRects[0])) {
646 if (rects) {
647 rects[0] = testRects[1];
648 rects[1] = testRects[0];
649 }
650 if (dirs) {
651 dirs[0] = testDirs[1];
652 dirs[1] = testDirs[0];
653 }
654 return true;
655 }
656 }
657 return false;
658 }
659
isOval(SkRect * bounds) const660 bool SkPath::isOval(SkRect* bounds) const {
661 return SkPathPriv::IsOval(*this, bounds, nullptr, nullptr);
662 }
663
isRRect(SkRRect * rrect) const664 bool SkPath::isRRect(SkRRect* rrect) const {
665 return SkPathPriv::IsRRect(*this, rrect, nullptr, nullptr);
666 }
667
countPoints() const668 int SkPath::countPoints() const {
669 return fPathRef->countPoints();
670 }
671
getPoints(SkPoint dst[],int max) const672 int SkPath::getPoints(SkPoint dst[], int max) const {
673 SkDEBUGCODE(this->validate();)
674
675 SkASSERT(max >= 0);
676 SkASSERT(!max || dst);
677 int count = SkMin32(max, fPathRef->countPoints());
678 sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
679 return fPathRef->countPoints();
680 }
681
getPoint(int index) const682 SkPoint SkPath::getPoint(int index) const {
683 if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
684 return fPathRef->atPoint(index);
685 }
686 return SkPoint::Make(0, 0);
687 }
688
countVerbs() const689 int SkPath::countVerbs() const {
690 return fPathRef->countVerbs();
691 }
692
copy_verbs_reverse(uint8_t * inorderDst,const uint8_t * reversedSrc,int count)693 static inline void copy_verbs_reverse(uint8_t* inorderDst,
694 const uint8_t* reversedSrc,
695 int count) {
696 for (int i = 0; i < count; ++i) {
697 inorderDst[i] = reversedSrc[~i];
698 }
699 }
700
getVerbs(uint8_t dst[],int max) const701 int SkPath::getVerbs(uint8_t dst[], int max) const {
702 SkDEBUGCODE(this->validate();)
703
704 SkASSERT(max >= 0);
705 SkASSERT(!max || dst);
706 int count = SkMin32(max, fPathRef->countVerbs());
707 copy_verbs_reverse(dst, fPathRef->verbs(), count);
708 return fPathRef->countVerbs();
709 }
710
approximateBytesUsed() const711 size_t SkPath::approximateBytesUsed() const {
712 size_t size = sizeof (SkPath);
713 if (fPathRef != nullptr) {
714 size += fPathRef->countPoints() * sizeof(SkPoint)
715 + fPathRef->countVerbs()
716 + fPathRef->countWeights() * sizeof(SkScalar);
717 }
718
719 return size;
720 }
721
getLastPt(SkPoint * lastPt) const722 bool SkPath::getLastPt(SkPoint* lastPt) const {
723 SkDEBUGCODE(this->validate();)
724
725 int count = fPathRef->countPoints();
726 if (count > 0) {
727 if (lastPt) {
728 *lastPt = fPathRef->atPoint(count - 1);
729 }
730 return true;
731 }
732 if (lastPt) {
733 lastPt->set(0, 0);
734 }
735 return false;
736 }
737
setPt(int index,SkScalar x,SkScalar y)738 void SkPath::setPt(int index, SkScalar x, SkScalar y) {
739 SkDEBUGCODE(this->validate();)
740
741 int count = fPathRef->countPoints();
742 if (count <= index) {
743 return;
744 } else {
745 SkPathRef::Editor ed(&fPathRef);
746 ed.atPoint(index)->set(x, y);
747 }
748 }
749
setLastPt(SkScalar x,SkScalar y)750 void SkPath::setLastPt(SkScalar x, SkScalar y) {
751 SkDEBUGCODE(this->validate();)
752
753 int count = fPathRef->countPoints();
754 if (count == 0) {
755 this->moveTo(x, y);
756 } else {
757 SkPathRef::Editor ed(&fPathRef);
758 ed.atPoint(count-1)->set(x, y);
759 }
760 }
761
762 // This is the public-facing non-const setConvexity().
setConvexity(Convexity c)763 void SkPath::setConvexity(Convexity c) {
764 fConvexity.store(c, std::memory_order_relaxed);
765 }
766
767 // Const hooks for working with fConvexity and fFirstDirection from const methods.
setConvexity(Convexity c) const768 void SkPath::setConvexity(Convexity c) const {
769 fConvexity.store(c, std::memory_order_relaxed);
770 }
setFirstDirection(uint8_t d) const771 void SkPath::setFirstDirection(uint8_t d) const {
772 fFirstDirection.store(d, std::memory_order_relaxed);
773 }
getFirstDirection() const774 uint8_t SkPath::getFirstDirection() const {
775 return fFirstDirection.load(std::memory_order_relaxed);
776 }
777
778 //////////////////////////////////////////////////////////////////////////////
779 // Construction methods
780
781 #define DIRTY_AFTER_EDIT \
782 do { \
783 this->setConvexity(kUnknown_Convexity); \
784 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection); \
785 } while (0)
786
incReserve(int inc)787 void SkPath::incReserve(int inc) {
788 SkDEBUGCODE(this->validate();)
789 if (inc > 0) {
790 SkPathRef::Editor(&fPathRef, inc, inc);
791 }
792 SkDEBUGCODE(this->validate();)
793 }
794
moveTo(SkScalar x,SkScalar y)795 SkPath& SkPath::moveTo(SkScalar x, SkScalar y) {
796 SkDEBUGCODE(this->validate();)
797
798 SkPathRef::Editor ed(&fPathRef);
799
800 // remember our index
801 fLastMoveToIndex = fPathRef->countPoints();
802
803 ed.growForVerb(kMove_Verb)->set(x, y);
804
805 DIRTY_AFTER_EDIT;
806 return *this;
807 }
808
rMoveTo(SkScalar x,SkScalar y)809 SkPath& SkPath::rMoveTo(SkScalar x, SkScalar y) {
810 SkPoint pt;
811 this->getLastPt(&pt);
812 return this->moveTo(pt.fX + x, pt.fY + y);
813 }
814
injectMoveToIfNeeded()815 void SkPath::injectMoveToIfNeeded() {
816 if (fLastMoveToIndex < 0) {
817 SkScalar x, y;
818 if (fPathRef->countVerbs() == 0) {
819 x = y = 0;
820 } else {
821 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
822 x = pt.fX;
823 y = pt.fY;
824 }
825 this->moveTo(x, y);
826 }
827 }
828
lineTo(SkScalar x,SkScalar y)829 SkPath& SkPath::lineTo(SkScalar x, SkScalar y) {
830 SkDEBUGCODE(this->validate();)
831
832 this->injectMoveToIfNeeded();
833
834 SkPathRef::Editor ed(&fPathRef);
835 ed.growForVerb(kLine_Verb)->set(x, y);
836
837 DIRTY_AFTER_EDIT;
838 return *this;
839 }
840
rLineTo(SkScalar x,SkScalar y)841 SkPath& SkPath::rLineTo(SkScalar x, SkScalar y) {
842 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
843 SkPoint pt;
844 this->getLastPt(&pt);
845 return this->lineTo(pt.fX + x, pt.fY + y);
846 }
847
quadTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)848 SkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
849 SkDEBUGCODE(this->validate();)
850
851 this->injectMoveToIfNeeded();
852
853 SkPathRef::Editor ed(&fPathRef);
854 SkPoint* pts = ed.growForVerb(kQuad_Verb);
855 pts[0].set(x1, y1);
856 pts[1].set(x2, y2);
857
858 DIRTY_AFTER_EDIT;
859 return *this;
860 }
861
rQuadTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)862 SkPath& SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
863 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
864 SkPoint pt;
865 this->getLastPt(&pt);
866 return this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
867 }
868
conicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar w)869 SkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
870 SkScalar w) {
871 // check for <= 0 or NaN with this test
872 if (!(w > 0)) {
873 this->lineTo(x2, y2);
874 } else if (!SkScalarIsFinite(w)) {
875 this->lineTo(x1, y1);
876 this->lineTo(x2, y2);
877 } else if (SK_Scalar1 == w) {
878 this->quadTo(x1, y1, x2, y2);
879 } else {
880 SkDEBUGCODE(this->validate();)
881
882 this->injectMoveToIfNeeded();
883
884 SkPathRef::Editor ed(&fPathRef);
885 SkPoint* pts = ed.growForVerb(kConic_Verb, w);
886 pts[0].set(x1, y1);
887 pts[1].set(x2, y2);
888
889 DIRTY_AFTER_EDIT;
890 }
891 return *this;
892 }
893
rConicTo(SkScalar dx1,SkScalar dy1,SkScalar dx2,SkScalar dy2,SkScalar w)894 SkPath& SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
895 SkScalar w) {
896 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
897 SkPoint pt;
898 this->getLastPt(&pt);
899 return this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
900 }
901
cubicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)902 SkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
903 SkScalar x3, SkScalar y3) {
904 SkDEBUGCODE(this->validate();)
905
906 this->injectMoveToIfNeeded();
907
908 SkPathRef::Editor ed(&fPathRef);
909 SkPoint* pts = ed.growForVerb(kCubic_Verb);
910 pts[0].set(x1, y1);
911 pts[1].set(x2, y2);
912 pts[2].set(x3, y3);
913
914 DIRTY_AFTER_EDIT;
915 return *this;
916 }
917
rCubicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)918 SkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
919 SkScalar x3, SkScalar y3) {
920 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
921 SkPoint pt;
922 this->getLastPt(&pt);
923 return this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
924 pt.fX + x3, pt.fY + y3);
925 }
926
close()927 SkPath& SkPath::close() {
928 SkDEBUGCODE(this->validate();)
929
930 int count = fPathRef->countVerbs();
931 if (count > 0) {
932 switch (fPathRef->atVerb(count - 1)) {
933 case kLine_Verb:
934 case kQuad_Verb:
935 case kConic_Verb:
936 case kCubic_Verb:
937 case kMove_Verb: {
938 SkPathRef::Editor ed(&fPathRef);
939 ed.growForVerb(kClose_Verb);
940 break;
941 }
942 case kClose_Verb:
943 // don't add a close if it's the first verb or a repeat
944 break;
945 default:
946 SkDEBUGFAIL("unexpected verb");
947 break;
948 }
949 }
950
951 // signal that we need a moveTo to follow us (unless we're done)
952 #if 0
953 if (fLastMoveToIndex >= 0) {
954 fLastMoveToIndex = ~fLastMoveToIndex;
955 }
956 #else
957 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
958 #endif
959 return *this;
960 }
961
962 ///////////////////////////////////////////////////////////////////////////////
963
964 namespace {
965
966 template <unsigned N>
967 class PointIterator {
968 public:
PointIterator(SkPath::Direction dir,unsigned startIndex)969 PointIterator(SkPath::Direction dir, unsigned startIndex)
970 : fCurrent(startIndex % N)
971 , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
972
current() const973 const SkPoint& current() const {
974 SkASSERT(fCurrent < N);
975 return fPts[fCurrent];
976 }
977
next()978 const SkPoint& next() {
979 fCurrent = (fCurrent + fAdvance) % N;
980 return this->current();
981 }
982
983 protected:
984 SkPoint fPts[N];
985
986 private:
987 unsigned fCurrent;
988 unsigned fAdvance;
989 };
990
991 class RectPointIterator : public PointIterator<4> {
992 public:
RectPointIterator(const SkRect & rect,SkPath::Direction dir,unsigned startIndex)993 RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
994 : PointIterator(dir, startIndex) {
995
996 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
997 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
998 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
999 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
1000 }
1001 };
1002
1003 class OvalPointIterator : public PointIterator<4> {
1004 public:
OvalPointIterator(const SkRect & oval,SkPath::Direction dir,unsigned startIndex)1005 OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
1006 : PointIterator(dir, startIndex) {
1007
1008 const SkScalar cx = oval.centerX();
1009 const SkScalar cy = oval.centerY();
1010
1011 fPts[0] = SkPoint::Make(cx, oval.fTop);
1012 fPts[1] = SkPoint::Make(oval.fRight, cy);
1013 fPts[2] = SkPoint::Make(cx, oval.fBottom);
1014 fPts[3] = SkPoint::Make(oval.fLeft, cy);
1015 }
1016 };
1017
1018 class RRectPointIterator : public PointIterator<8> {
1019 public:
RRectPointIterator(const SkRRect & rrect,SkPath::Direction dir,unsigned startIndex)1020 RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
1021 : PointIterator(dir, startIndex) {
1022
1023 const SkRect& bounds = rrect.getBounds();
1024 const SkScalar L = bounds.fLeft;
1025 const SkScalar T = bounds.fTop;
1026 const SkScalar R = bounds.fRight;
1027 const SkScalar B = bounds.fBottom;
1028
1029 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
1030 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
1031 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
1032 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
1033 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
1034 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
1035 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
1036 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
1037 }
1038 };
1039
1040 } // anonymous namespace
1041
assert_known_direction(int dir)1042 static void assert_known_direction(int dir) {
1043 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
1044 }
1045
addRect(const SkRect & rect,Direction dir)1046 SkPath& SkPath::addRect(const SkRect& rect, Direction dir) {
1047 return this->addRect(rect, dir, 0);
1048 }
1049
addRect(SkScalar left,SkScalar top,SkScalar right,SkScalar bottom,Direction dir)1050 SkPath& SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
1051 SkScalar bottom, Direction dir) {
1052 return this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
1053 }
1054
addRect(const SkRect & rect,Direction dir,unsigned startIndex)1055 SkPath& SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
1056 assert_known_direction(dir);
1057 this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathPriv::FirstDirection)dir
1058 : SkPathPriv::kUnknown_FirstDirection);
1059 SkAutoDisableDirectionCheck addc(this);
1060 SkAutoPathBoundsUpdate apbu(this, rect);
1061
1062 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1063
1064 const int kVerbs = 5; // moveTo + 3x lineTo + close
1065 this->incReserve(kVerbs);
1066
1067 RectPointIterator iter(rect, dir, startIndex);
1068
1069 this->moveTo(iter.current());
1070 this->lineTo(iter.next());
1071 this->lineTo(iter.next());
1072 this->lineTo(iter.next());
1073 this->close();
1074
1075 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1076 return *this;
1077 }
1078
addPoly(const SkPoint pts[],int count,bool close)1079 SkPath& SkPath::addPoly(const SkPoint pts[], int count, bool close) {
1080 SkDEBUGCODE(this->validate();)
1081 if (count <= 0) {
1082 return *this;
1083 }
1084
1085 fLastMoveToIndex = fPathRef->countPoints();
1086
1087 // +close makes room for the extra kClose_Verb
1088 SkPathRef::Editor ed(&fPathRef, count+close, count);
1089
1090 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
1091 if (count > 1) {
1092 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
1093 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
1094 }
1095
1096 if (close) {
1097 ed.growForVerb(kClose_Verb);
1098 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
1099 }
1100
1101 DIRTY_AFTER_EDIT;
1102 SkDEBUGCODE(this->validate();)
1103 return *this;
1104 }
1105
1106 #include "src/core/SkGeometry.h"
1107
arc_is_lone_point(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,SkPoint * pt)1108 static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1109 SkPoint* pt) {
1110 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
1111 // Chrome uses this path to move into and out of ovals. If not
1112 // treated as a special case the moves can distort the oval's
1113 // bounding box (and break the circle special case).
1114 pt->set(oval.fRight, oval.centerY());
1115 return true;
1116 } else if (0 == oval.width() && 0 == oval.height()) {
1117 // Chrome will sometimes create 0 radius round rects. Having degenerate
1118 // quad segments in the path prevents the path from being recognized as
1119 // a rect.
1120 // TODO: optimizing the case where only one of width or height is zero
1121 // should also be considered. This case, however, doesn't seem to be
1122 // as common as the single point case.
1123 pt->set(oval.fRight, oval.fTop);
1124 return true;
1125 }
1126 return false;
1127 }
1128
1129 // Return the unit vectors pointing at the start/stop points for the given start/sweep angles
1130 //
angles_to_unit_vectors(SkScalar startAngle,SkScalar sweepAngle,SkVector * startV,SkVector * stopV,SkRotationDirection * dir)1131 static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
1132 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
1133 SkScalar startRad = SkDegreesToRadians(startAngle),
1134 stopRad = SkDegreesToRadians(startAngle + sweepAngle);
1135
1136 startV->fY = SkScalarSinSnapToZero(startRad);
1137 startV->fX = SkScalarCosSnapToZero(startRad);
1138 stopV->fY = SkScalarSinSnapToZero(stopRad);
1139 stopV->fX = SkScalarCosSnapToZero(stopRad);
1140
1141 /* If the sweep angle is nearly (but less than) 360, then due to precision
1142 loss in radians-conversion and/or sin/cos, we may end up with coincident
1143 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
1144 of drawing a nearly complete circle (good).
1145 e.g. canvas.drawArc(0, 359.99, ...)
1146 -vs- canvas.drawArc(0, 359.9, ...)
1147 We try to detect this edge case, and tweak the stop vector
1148 */
1149 if (*startV == *stopV) {
1150 SkScalar sw = SkScalarAbs(sweepAngle);
1151 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
1152 // make a guess at a tiny angle (in radians) to tweak by
1153 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
1154 // not sure how much will be enough, so we use a loop
1155 do {
1156 stopRad -= deltaRad;
1157 stopV->fY = SkScalarSinSnapToZero(stopRad);
1158 stopV->fX = SkScalarCosSnapToZero(stopRad);
1159 } while (*startV == *stopV);
1160 }
1161 }
1162 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
1163 }
1164
1165 /**
1166 * If this returns 0, then the caller should just line-to the singlePt, else it should
1167 * ignore singlePt and append the specified number of conics.
1168 */
build_arc_conics(const SkRect & oval,const SkVector & start,const SkVector & stop,SkRotationDirection dir,SkConic conics[SkConic::kMaxConicsForArc],SkPoint * singlePt)1169 static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
1170 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
1171 SkPoint* singlePt) {
1172 SkMatrix matrix;
1173
1174 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
1175 matrix.postTranslate(oval.centerX(), oval.centerY());
1176
1177 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
1178 if (0 == count) {
1179 matrix.mapXY(stop.x(), stop.y(), singlePt);
1180 }
1181 return count;
1182 }
1183
addRoundRect(const SkRect & rect,const SkScalar radii[],Direction dir)1184 SkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
1185 Direction dir) {
1186 SkRRect rrect;
1187 rrect.setRectRadii(rect, (const SkVector*) radii);
1188 return this->addRRect(rrect, dir);
1189 }
1190
addRRect(const SkRRect & rrect,Direction dir)1191 SkPath& SkPath::addRRect(const SkRRect& rrect, Direction dir) {
1192 // legacy start indices: 6 (CW) and 7(CCW)
1193 return this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
1194 }
1195
addRRect(const SkRRect & rrect,Direction dir,unsigned startIndex)1196 SkPath& SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
1197 assert_known_direction(dir);
1198
1199 bool isRRect = hasOnlyMoveTos();
1200 const SkRect& bounds = rrect.getBounds();
1201
1202 if (rrect.isRect() || rrect.isEmpty()) {
1203 // degenerate(rect) => radii points are collapsing
1204 this->addRect(bounds, dir, (startIndex + 1) / 2);
1205 } else if (rrect.isOval()) {
1206 // degenerate(oval) => line points are collapsing
1207 this->addOval(bounds, dir, startIndex / 2);
1208 } else {
1209 this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathPriv::FirstDirection)dir
1210 : SkPathPriv::kUnknown_FirstDirection);
1211
1212 SkAutoPathBoundsUpdate apbu(this, bounds);
1213 SkAutoDisableDirectionCheck addc(this);
1214
1215 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
1216 const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
1217 const SkScalar weight = SK_ScalarRoot2Over2;
1218
1219 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1220 const int kVerbs = startsWithConic
1221 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
1222 : 10; // moveTo + 4x lineTo + 4x conicTo + close
1223 this->incReserve(kVerbs);
1224
1225 RRectPointIterator rrectIter(rrect, dir, startIndex);
1226 // Corner iterator indices follow the collapsed radii model,
1227 // adjusted such that the start pt is "behind" the radii start pt.
1228 const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
1229 RectPointIterator rectIter(bounds, dir, rectStartIndex);
1230
1231 this->moveTo(rrectIter.current());
1232 if (startsWithConic) {
1233 for (unsigned i = 0; i < 3; ++i) {
1234 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1235 this->lineTo(rrectIter.next());
1236 }
1237 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1238 // final lineTo handled by close().
1239 } else {
1240 for (unsigned i = 0; i < 4; ++i) {
1241 this->lineTo(rrectIter.next());
1242 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1243 }
1244 }
1245 this->close();
1246
1247 SkPathRef::Editor ed(&fPathRef);
1248 ed.setIsRRect(isRRect, dir, startIndex % 8);
1249
1250 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1251 }
1252
1253 SkDEBUGCODE(fPathRef->validate();)
1254 return *this;
1255 }
1256
hasOnlyMoveTos() const1257 bool SkPath::hasOnlyMoveTos() const {
1258 int count = fPathRef->countVerbs();
1259 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
1260 for (int i = 0; i < count; ++i) {
1261 if (*verbs == kLine_Verb ||
1262 *verbs == kQuad_Verb ||
1263 *verbs == kConic_Verb ||
1264 *verbs == kCubic_Verb) {
1265 return false;
1266 }
1267 ++verbs;
1268 }
1269 return true;
1270 }
1271
isZeroLengthSincePoint(int startPtIndex) const1272 bool SkPath::isZeroLengthSincePoint(int startPtIndex) const {
1273 int count = fPathRef->countPoints() - startPtIndex;
1274 if (count < 2) {
1275 return true;
1276 }
1277 const SkPoint* pts = fPathRef.get()->points() + startPtIndex;
1278 const SkPoint& first = *pts;
1279 for (int index = 1; index < count; ++index) {
1280 if (first != pts[index]) {
1281 return false;
1282 }
1283 }
1284 return true;
1285 }
1286
addRoundRect(const SkRect & rect,SkScalar rx,SkScalar ry,Direction dir)1287 SkPath& SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1288 Direction dir) {
1289 assert_known_direction(dir);
1290
1291 if (rx < 0 || ry < 0) {
1292 return *this;
1293 }
1294
1295 SkRRect rrect;
1296 rrect.setRectXY(rect, rx, ry);
1297 return this->addRRect(rrect, dir);
1298 }
1299
addOval(const SkRect & oval,Direction dir)1300 SkPath& SkPath::addOval(const SkRect& oval, Direction dir) {
1301 // legacy start index: 1
1302 return this->addOval(oval, dir, 1);
1303 }
1304
addOval(const SkRect & oval,Direction dir,unsigned startPointIndex)1305 SkPath& SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
1306 assert_known_direction(dir);
1307
1308 /* If addOval() is called after previous moveTo(),
1309 this path is still marked as an oval. This is used to
1310 fit into WebKit's calling sequences.
1311 We can't simply check isEmpty() in this case, as additional
1312 moveTo() would mark the path non empty.
1313 */
1314 bool isOval = hasOnlyMoveTos();
1315 if (isOval) {
1316 this->setFirstDirection((SkPathPriv::FirstDirection)dir);
1317 } else {
1318 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1319 }
1320
1321 SkAutoDisableDirectionCheck addc(this);
1322 SkAutoPathBoundsUpdate apbu(this, oval);
1323
1324 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1325 const int kVerbs = 6; // moveTo + 4x conicTo + close
1326 this->incReserve(kVerbs);
1327
1328 OvalPointIterator ovalIter(oval, dir, startPointIndex);
1329 // The corner iterator pts are tracking "behind" the oval/radii pts.
1330 RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
1331 const SkScalar weight = SK_ScalarRoot2Over2;
1332
1333 this->moveTo(ovalIter.current());
1334 for (unsigned i = 0; i < 4; ++i) {
1335 this->conicTo(rectIter.next(), ovalIter.next(), weight);
1336 }
1337 this->close();
1338
1339 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1340
1341 SkPathRef::Editor ed(&fPathRef);
1342
1343 ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4);
1344 return *this;
1345 }
1346
addCircle(SkScalar x,SkScalar y,SkScalar r,Direction dir)1347 SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
1348 if (r > 0) {
1349 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1350 }
1351 return *this;
1352 }
1353
arcTo(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool forceMoveTo)1354 SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1355 bool forceMoveTo) {
1356 if (oval.width() < 0 || oval.height() < 0) {
1357 return *this;
1358 }
1359
1360 if (fPathRef->countVerbs() == 0) {
1361 forceMoveTo = true;
1362 }
1363
1364 SkPoint lonePt;
1365 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1366 return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1367 }
1368
1369 SkVector startV, stopV;
1370 SkRotationDirection dir;
1371 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1372
1373 SkPoint singlePt;
1374
1375 // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
1376 // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
1377 // arcs from the same oval.
1378 auto addPt = [&forceMoveTo, this](const SkPoint& pt) {
1379 SkPoint lastPt;
1380 if (forceMoveTo) {
1381 this->moveTo(pt);
1382 } else if (!this->getLastPt(&lastPt) ||
1383 !SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
1384 !SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
1385 this->lineTo(pt);
1386 }
1387 };
1388
1389 // At this point, we know that the arc is not a lone point, but startV == stopV
1390 // indicates that the sweepAngle is too small such that angles_to_unit_vectors
1391 // cannot handle it.
1392 if (startV == stopV) {
1393 SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
1394 SkScalar radiusX = oval.width() / 2;
1395 SkScalar radiusY = oval.height() / 2;
1396 // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle
1397 // is very small and radius is huge, the expected behavior here is to draw a line. But
1398 // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot.
1399 singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle),
1400 oval.centerY() + radiusY * SkScalarSin(endAngle));
1401 addPt(singlePt);
1402 return *this;
1403 }
1404
1405 SkConic conics[SkConic::kMaxConicsForArc];
1406 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1407 if (count) {
1408 this->incReserve(count * 2 + 1);
1409 const SkPoint& pt = conics[0].fPts[0];
1410 addPt(pt);
1411 for (int i = 0; i < count; ++i) {
1412 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1413 }
1414 } else {
1415 addPt(singlePt);
1416 }
1417 return *this;
1418 }
1419
1420 // This converts the SVG arc to conics.
1421 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
1422 // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
1423 // See also SVG implementation notes:
1424 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1425 // Note that arcSweep bool value is flipped from the original implementation.
arcTo(SkScalar rx,SkScalar ry,SkScalar angle,SkPath::ArcSize arcLarge,SkPath::Direction arcSweep,SkScalar x,SkScalar y)1426 SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
1427 SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
1428 this->injectMoveToIfNeeded();
1429 SkPoint srcPts[2];
1430 this->getLastPt(&srcPts[0]);
1431 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
1432 // joining the endpoints.
1433 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
1434 if (!rx || !ry) {
1435 return this->lineTo(x, y);
1436 }
1437 // If the current point and target point for the arc are identical, it should be treated as a
1438 // zero length path. This ensures continuity in animations.
1439 srcPts[1].set(x, y);
1440 if (srcPts[0] == srcPts[1]) {
1441 return this->lineTo(x, y);
1442 }
1443 rx = SkScalarAbs(rx);
1444 ry = SkScalarAbs(ry);
1445 SkVector midPointDistance = srcPts[0] - srcPts[1];
1446 midPointDistance *= 0.5f;
1447
1448 SkMatrix pointTransform;
1449 pointTransform.setRotate(-angle);
1450
1451 SkPoint transformedMidPoint;
1452 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
1453 SkScalar squareRx = rx * rx;
1454 SkScalar squareRy = ry * ry;
1455 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
1456 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
1457
1458 // Check if the radii are big enough to draw the arc, scale radii if not.
1459 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
1460 SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
1461 if (radiiScale > 1) {
1462 radiiScale = SkScalarSqrt(radiiScale);
1463 rx *= radiiScale;
1464 ry *= radiiScale;
1465 }
1466
1467 pointTransform.setScale(1 / rx, 1 / ry);
1468 pointTransform.preRotate(-angle);
1469
1470 SkPoint unitPts[2];
1471 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
1472 SkVector delta = unitPts[1] - unitPts[0];
1473
1474 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
1475 SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
1476
1477 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
1478 if (SkToBool(arcSweep) != SkToBool(arcLarge)) { // flipped from the original implementation
1479 scaleFactor = -scaleFactor;
1480 }
1481 delta.scale(scaleFactor);
1482 SkPoint centerPoint = unitPts[0] + unitPts[1];
1483 centerPoint *= 0.5f;
1484 centerPoint.offset(-delta.fY, delta.fX);
1485 unitPts[0] -= centerPoint;
1486 unitPts[1] -= centerPoint;
1487 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
1488 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
1489 SkScalar thetaArc = theta2 - theta1;
1490 if (thetaArc < 0 && !arcSweep) { // arcSweep flipped from the original implementation
1491 thetaArc += SK_ScalarPI * 2;
1492 } else if (thetaArc > 0 && arcSweep) { // arcSweep flipped from the original implementation
1493 thetaArc -= SK_ScalarPI * 2;
1494 }
1495
1496 // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272)
1497 // so we do a quick check here. The precise tolerance amount is just made up.
1498 // PI/million happens to fix the bug in 9272, but a larger value is probably
1499 // ok too.
1500 if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) {
1501 return this->lineTo(x, y);
1502 }
1503
1504 pointTransform.setRotate(angle);
1505 pointTransform.preScale(rx, ry);
1506
1507 // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
1508 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
1509 SkScalar thetaWidth = thetaArc / segments;
1510 SkScalar t = SkScalarTan(0.5f * thetaWidth);
1511 if (!SkScalarIsFinite(t)) {
1512 return *this;
1513 }
1514 SkScalar startTheta = theta1;
1515 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
1516 auto scalar_is_integer = [](SkScalar scalar) -> bool {
1517 return scalar == SkScalarFloorToScalar(scalar);
1518 };
1519 bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
1520 scalar_is_integer(rx) && scalar_is_integer(ry) &&
1521 scalar_is_integer(x) && scalar_is_integer(y);
1522
1523 for (int i = 0; i < segments; ++i) {
1524 SkScalar endTheta = startTheta + thetaWidth,
1525 sinEndTheta = SkScalarSinSnapToZero(endTheta),
1526 cosEndTheta = SkScalarCosSnapToZero(endTheta);
1527
1528 unitPts[1].set(cosEndTheta, sinEndTheta);
1529 unitPts[1] += centerPoint;
1530 unitPts[0] = unitPts[1];
1531 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
1532 SkPoint mapped[2];
1533 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
1534 /*
1535 Computing the arc width introduces rounding errors that cause arcs to start
1536 outside their marks. A round rect may lose convexity as a result. If the input
1537 values are on integers, place the conic on integers as well.
1538 */
1539 if (expectIntegers) {
1540 SkScalar* mappedScalars = &mapped[0].fX;
1541 for (unsigned index = 0; index < sizeof(mapped) / sizeof(SkScalar); ++index) {
1542 mappedScalars[index] = SkScalarRoundToScalar(mappedScalars[index]);
1543 }
1544 }
1545 this->conicTo(mapped[0], mapped[1], w);
1546 startTheta = endTheta;
1547 }
1548 return *this;
1549 }
1550
rArcTo(SkScalar rx,SkScalar ry,SkScalar xAxisRotate,SkPath::ArcSize largeArc,SkPath::Direction sweep,SkScalar dx,SkScalar dy)1551 SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
1552 SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
1553 SkPoint currentPoint;
1554 this->getLastPt(¤tPoint);
1555 return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep,
1556 currentPoint.fX + dx, currentPoint.fY + dy);
1557 }
1558
addArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle)1559 SkPath& SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
1560 if (oval.isEmpty() || 0 == sweepAngle) {
1561 return *this;
1562 }
1563
1564 const SkScalar kFullCircleAngle = SkIntToScalar(360);
1565
1566 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
1567 // We can treat the arc as an oval if it begins at one of our legal starting positions.
1568 // See SkPath::addOval() docs.
1569 SkScalar startOver90 = startAngle / 90.f;
1570 SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
1571 SkScalar error = startOver90 - startOver90I;
1572 if (SkScalarNearlyEqual(error, 0)) {
1573 // Index 1 is at startAngle == 0.
1574 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
1575 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
1576 return this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction,
1577 (unsigned) startIndex);
1578 }
1579 }
1580 return this->arcTo(oval, startAngle, sweepAngle, true);
1581 }
1582
1583 /*
1584 Need to handle the case when the angle is sharp, and our computed end-points
1585 for the arc go behind pt1 and/or p2...
1586 */
arcTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar radius)1587 SkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1588 if (radius == 0) {
1589 return this->lineTo(x1, y1);
1590 }
1591
1592 // need to know our prev pt so we can construct tangent vectors
1593 SkPoint start;
1594 this->getLastPt(&start);
1595
1596 // need double precision for these calcs.
1597 SkDVector befored, afterd;
1598 befored.set({x1 - start.fX, y1 - start.fY}).normalize();
1599 afterd.set({x2 - x1, y2 - y1}).normalize();
1600 double cosh = befored.dot(afterd);
1601 double sinh = befored.cross(afterd);
1602
1603 if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) {
1604 return this->lineTo(x1, y1);
1605 }
1606
1607 // safe to convert back to floats now
1608 SkVector before = befored.asSkVector();
1609 SkVector after = afterd.asSkVector();
1610 SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh));
1611 SkScalar xx = x1 - dist * before.fX;
1612 SkScalar yy = y1 - dist * before.fY;
1613 after.setLength(dist);
1614 this->lineTo(xx, yy);
1615 SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5));
1616 return this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
1617 }
1618
1619 ///////////////////////////////////////////////////////////////////////////////
1620
addPath(const SkPath & path,SkScalar dx,SkScalar dy,AddPathMode mode)1621 SkPath& SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
1622 SkMatrix matrix;
1623
1624 matrix.setTranslate(dx, dy);
1625 return this->addPath(path, matrix, mode);
1626 }
1627
addPath(const SkPath & srcPath,const SkMatrix & matrix,AddPathMode mode)1628 SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) {
1629 // Detect if we're trying to add ourself
1630 const SkPath* src = &srcPath;
1631 SkTLazy<SkPath> tmp;
1632 if (this == src) {
1633 src = tmp.set(srcPath);
1634 }
1635
1636 SkPathRef::Editor(&fPathRef, src->countVerbs(), src->countPoints());
1637
1638 RawIter iter(*src);
1639 SkPoint pts[4];
1640 Verb verb;
1641
1642 SkMatrixPriv::MapPtsProc proc = SkMatrixPriv::GetMapPtsProc(matrix);
1643 bool firstVerb = true;
1644 while ((verb = iter.next(pts)) != kDone_Verb) {
1645 switch (verb) {
1646 case kMove_Verb:
1647 proc(matrix, &pts[0], &pts[0], 1);
1648 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
1649 injectMoveToIfNeeded(); // In case last contour is closed
1650 SkPoint lastPt;
1651 // don't add lineTo if it is degenerate
1652 if (fLastMoveToIndex < 0 || !this->getLastPt(&lastPt) || lastPt != pts[0]) {
1653 this->lineTo(pts[0]);
1654 }
1655 } else {
1656 this->moveTo(pts[0]);
1657 }
1658 break;
1659 case kLine_Verb:
1660 proc(matrix, &pts[1], &pts[1], 1);
1661 this->lineTo(pts[1]);
1662 break;
1663 case kQuad_Verb:
1664 proc(matrix, &pts[1], &pts[1], 2);
1665 this->quadTo(pts[1], pts[2]);
1666 break;
1667 case kConic_Verb:
1668 proc(matrix, &pts[1], &pts[1], 2);
1669 this->conicTo(pts[1], pts[2], iter.conicWeight());
1670 break;
1671 case kCubic_Verb:
1672 proc(matrix, &pts[1], &pts[1], 3);
1673 this->cubicTo(pts[1], pts[2], pts[3]);
1674 break;
1675 case kClose_Verb:
1676 this->close();
1677 break;
1678 default:
1679 SkDEBUGFAIL("unknown verb");
1680 }
1681 firstVerb = false;
1682 }
1683 return *this;
1684 }
1685
1686 ///////////////////////////////////////////////////////////////////////////////
1687
pts_in_verb(unsigned verb)1688 static int pts_in_verb(unsigned verb) {
1689 static const uint8_t gPtsInVerb[] = {
1690 1, // kMove
1691 1, // kLine
1692 2, // kQuad
1693 2, // kConic
1694 3, // kCubic
1695 0, // kClose
1696 0 // kDone
1697 };
1698
1699 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
1700 return gPtsInVerb[verb];
1701 }
1702
1703 // ignore the last point of the 1st contour
reversePathTo(const SkPath & path)1704 SkPath& SkPath::reversePathTo(const SkPath& path) {
1705 const uint8_t* verbs = path.fPathRef->verbsMemBegin(); // points at the last verb
1706 if (!verbs) { // empty path returns nullptr
1707 return *this;
1708 }
1709 const uint8_t* verbsEnd = path.fPathRef->verbs() - 1; // points just past the first verb
1710 SkASSERT(verbsEnd[0] == kMove_Verb);
1711 const SkPoint* pts = path.fPathRef->pointsEnd() - 1;
1712 const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
1713
1714 while (verbs < verbsEnd) {
1715 uint8_t v = *verbs++;
1716 pts -= pts_in_verb(v);
1717 switch (v) {
1718 case kMove_Verb:
1719 // if the path has multiple contours, stop after reversing the last
1720 return *this;
1721 case kLine_Verb:
1722 this->lineTo(pts[0]);
1723 break;
1724 case kQuad_Verb:
1725 this->quadTo(pts[1], pts[0]);
1726 break;
1727 case kConic_Verb:
1728 this->conicTo(pts[1], pts[0], *--conicWeights);
1729 break;
1730 case kCubic_Verb:
1731 this->cubicTo(pts[2], pts[1], pts[0]);
1732 break;
1733 case kClose_Verb:
1734 SkASSERT(verbs - path.fPathRef->verbsMemBegin() == 1);
1735 break;
1736 default:
1737 SkDEBUGFAIL("bad verb");
1738 break;
1739 }
1740 }
1741 return *this;
1742 }
1743
reverseAddPath(const SkPath & srcPath)1744 SkPath& SkPath::reverseAddPath(const SkPath& srcPath) {
1745 // Detect if we're trying to add ourself
1746 const SkPath* src = &srcPath;
1747 SkTLazy<SkPath> tmp;
1748 if (this == src) {
1749 src = tmp.set(srcPath);
1750 }
1751
1752 SkPathRef::Editor ed(&fPathRef, src->countVerbs(), src->countPoints());
1753
1754 const SkPoint* pts = src->fPathRef->pointsEnd();
1755 // we will iterate through src's verbs backwards
1756 const uint8_t* verbs = src->fPathRef->verbsMemBegin(); // points at the last verb
1757 const uint8_t* verbsEnd = src->fPathRef->verbs(); // points just past the first verb
1758 const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd();
1759
1760 bool needMove = true;
1761 bool needClose = false;
1762 while (verbs < verbsEnd) {
1763 uint8_t v = *(verbs++);
1764 int n = pts_in_verb(v);
1765
1766 if (needMove) {
1767 --pts;
1768 this->moveTo(pts->fX, pts->fY);
1769 needMove = false;
1770 }
1771 pts -= n;
1772 switch (v) {
1773 case kMove_Verb:
1774 if (needClose) {
1775 this->close();
1776 needClose = false;
1777 }
1778 needMove = true;
1779 pts += 1; // so we see the point in "if (needMove)" above
1780 break;
1781 case kLine_Verb:
1782 this->lineTo(pts[0]);
1783 break;
1784 case kQuad_Verb:
1785 this->quadTo(pts[1], pts[0]);
1786 break;
1787 case kConic_Verb:
1788 this->conicTo(pts[1], pts[0], *--conicWeights);
1789 break;
1790 case kCubic_Verb:
1791 this->cubicTo(pts[2], pts[1], pts[0]);
1792 break;
1793 case kClose_Verb:
1794 needClose = true;
1795 break;
1796 default:
1797 SkDEBUGFAIL("unexpected verb");
1798 }
1799 }
1800 return *this;
1801 }
1802
1803 ///////////////////////////////////////////////////////////////////////////////
1804
offset(SkScalar dx,SkScalar dy,SkPath * dst) const1805 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1806 SkMatrix matrix;
1807
1808 matrix.setTranslate(dx, dy);
1809 this->transform(matrix, dst);
1810 }
1811
subdivide_cubic_to(SkPath * path,const SkPoint pts[4],int level=2)1812 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1813 int level = 2) {
1814 if (--level >= 0) {
1815 SkPoint tmp[7];
1816
1817 SkChopCubicAtHalf(pts, tmp);
1818 subdivide_cubic_to(path, &tmp[0], level);
1819 subdivide_cubic_to(path, &tmp[3], level);
1820 } else {
1821 path->cubicTo(pts[1], pts[2], pts[3]);
1822 }
1823 }
1824
transform(const SkMatrix & matrix,SkPath * dst) const1825 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
1826 if (matrix.isIdentity()) {
1827 if (dst != nullptr && dst != this) {
1828 *dst = *this;
1829 }
1830 return;
1831 }
1832
1833 SkDEBUGCODE(this->validate();)
1834 if (dst == nullptr) {
1835 dst = (SkPath*)this;
1836 }
1837
1838 if (matrix.hasPerspective()) {
1839 SkPath tmp;
1840 tmp.fFillType = fFillType;
1841
1842 SkPath::Iter iter(*this, false);
1843 SkPoint pts[4];
1844 SkPath::Verb verb;
1845
1846 while ((verb = iter.next(pts)) != kDone_Verb) {
1847 switch (verb) {
1848 case kMove_Verb:
1849 tmp.moveTo(pts[0]);
1850 break;
1851 case kLine_Verb:
1852 tmp.lineTo(pts[1]);
1853 break;
1854 case kQuad_Verb:
1855 // promote the quad to a conic
1856 tmp.conicTo(pts[1], pts[2],
1857 SkConic::TransformW(pts, SK_Scalar1, matrix));
1858 break;
1859 case kConic_Verb:
1860 tmp.conicTo(pts[1], pts[2],
1861 SkConic::TransformW(pts, iter.conicWeight(), matrix));
1862 break;
1863 case kCubic_Verb:
1864 subdivide_cubic_to(&tmp, pts);
1865 break;
1866 case kClose_Verb:
1867 tmp.close();
1868 break;
1869 default:
1870 SkDEBUGFAIL("unknown verb");
1871 break;
1872 }
1873 }
1874
1875 dst->swap(tmp);
1876 SkPathRef::Editor ed(&dst->fPathRef);
1877 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
1878 dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1879 } else {
1880 Convexity convexity = this->getConvexityOrUnknown();
1881
1882 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
1883
1884 if (this != dst) {
1885 dst->fLastMoveToIndex = fLastMoveToIndex;
1886 dst->fFillType = fFillType;
1887 dst->fIsVolatile = fIsVolatile;
1888 }
1889
1890 // Due to finite/fragile float numerics, we can't assume that a convex path remains
1891 // convex after a transformation, so mark it as unknown here.
1892 // However, some transformations are thought to be safe:
1893 // axis-aligned values under scale/translate.
1894 //
1895 // See skbug.com/8606
1896 // If we can land a robust convex scan-converter, we may be able to relax/remove this
1897 // check, and keep convex paths marked as such after a general transform...
1898 //
1899 if (matrix.isScaleTranslate() && SkPathPriv::IsAxisAligned(*this)) {
1900 dst->setConvexity(convexity);
1901 } else {
1902 dst->setConvexity(kUnknown_Convexity);
1903 }
1904
1905 if (this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
1906 dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1907 } else {
1908 SkScalar det2x2 =
1909 matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) -
1910 matrix.get(SkMatrix::kMSkewX) * matrix.get(SkMatrix::kMSkewY);
1911 if (det2x2 < 0) {
1912 dst->setFirstDirection(
1913 SkPathPriv::OppositeFirstDirection(
1914 (SkPathPriv::FirstDirection)this->getFirstDirection()));
1915 } else if (det2x2 > 0) {
1916 dst->setFirstDirection(this->getFirstDirection());
1917 } else {
1918 dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
1919 }
1920 }
1921
1922 SkDEBUGCODE(dst->validate();)
1923 }
1924 }
1925
1926 ///////////////////////////////////////////////////////////////////////////////
1927 ///////////////////////////////////////////////////////////////////////////////
1928
Iter()1929 SkPath::Iter::Iter() {
1930 #ifdef SK_DEBUG
1931 fPts = nullptr;
1932 fConicWeights = nullptr;
1933 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1934 fForceClose = fCloseLine = false;
1935 fSegmentState = kEmptyContour_SegmentState;
1936 #endif
1937 // need to init enough to make next() harmlessly return kDone_Verb
1938 fVerbs = nullptr;
1939 fVerbStop = nullptr;
1940 fNeedClose = false;
1941 }
1942
Iter(const SkPath & path,bool forceClose)1943 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1944 this->setPath(path, forceClose);
1945 }
1946
setPath(const SkPath & path,bool forceClose)1947 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1948 fPts = path.fPathRef->points();
1949 fVerbs = path.fPathRef->verbs();
1950 fVerbStop = path.fPathRef->verbsMemBegin();
1951 fConicWeights = path.fPathRef->conicWeights();
1952 if (fConicWeights) {
1953 fConicWeights -= 1; // begin one behind
1954 }
1955 fLastPt.fX = fLastPt.fY = 0;
1956 fMoveTo.fX = fMoveTo.fY = 0;
1957 fForceClose = SkToU8(forceClose);
1958 fNeedClose = false;
1959 fSegmentState = kEmptyContour_SegmentState;
1960 }
1961
isClosedContour() const1962 bool SkPath::Iter::isClosedContour() const {
1963 if (fVerbs == nullptr || fVerbs == fVerbStop) {
1964 return false;
1965 }
1966 if (fForceClose) {
1967 return true;
1968 }
1969
1970 const uint8_t* verbs = fVerbs;
1971 const uint8_t* stop = fVerbStop;
1972
1973 if (kMove_Verb == *(verbs - 1)) {
1974 verbs -= 1; // skip the initial moveto
1975 }
1976
1977 while (verbs > stop) {
1978 // verbs points one beyond the current verb, decrement first.
1979 unsigned v = *(--verbs);
1980 if (kMove_Verb == v) {
1981 break;
1982 }
1983 if (kClose_Verb == v) {
1984 return true;
1985 }
1986 }
1987 return false;
1988 }
1989
autoClose(SkPoint pts[2])1990 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1991 SkASSERT(pts);
1992 if (fLastPt != fMoveTo) {
1993 // A special case: if both points are NaN, SkPoint::operation== returns
1994 // false, but the iterator expects that they are treated as the same.
1995 // (consider SkPoint is a 2-dimension float point).
1996 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
1997 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
1998 return kClose_Verb;
1999 }
2000
2001 pts[0] = fLastPt;
2002 pts[1] = fMoveTo;
2003 fLastPt = fMoveTo;
2004 fCloseLine = true;
2005 return kLine_Verb;
2006 } else {
2007 pts[0] = fMoveTo;
2008 return kClose_Verb;
2009 }
2010 }
2011
cons_moveTo()2012 const SkPoint& SkPath::Iter::cons_moveTo() {
2013 if (fSegmentState == kAfterMove_SegmentState) {
2014 // Set the first return pt to the move pt
2015 fSegmentState = kAfterPrimitive_SegmentState;
2016 return fMoveTo;
2017 }
2018
2019 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
2020 // Set the first return pt to the last pt of the previous primitive.
2021 return fPts[-1];
2022 }
2023
next(SkPoint ptsParam[4])2024 SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) {
2025 SkASSERT(ptsParam);
2026
2027 if (fVerbs == fVerbStop) {
2028 // Close the curve if requested and if there is some curve to close
2029 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
2030 if (kLine_Verb == this->autoClose(ptsParam)) {
2031 return kLine_Verb;
2032 }
2033 fNeedClose = false;
2034 return kClose_Verb;
2035 }
2036 return kDone_Verb;
2037 }
2038
2039 // fVerbs is one beyond the current verb, decrement first
2040 unsigned verb = *(--fVerbs);
2041 const SkPoint* SK_RESTRICT srcPts = fPts;
2042 SkPoint* SK_RESTRICT pts = ptsParam;
2043
2044 switch (verb) {
2045 case kMove_Verb:
2046 if (fNeedClose) {
2047 fVerbs++; // move back one verb
2048 verb = this->autoClose(pts);
2049 if (verb == kClose_Verb) {
2050 fNeedClose = false;
2051 }
2052 return (Verb)verb;
2053 }
2054 if (fVerbs == fVerbStop) { // might be a trailing moveto
2055 return kDone_Verb;
2056 }
2057 fMoveTo = *srcPts;
2058 pts[0] = *srcPts;
2059 srcPts += 1;
2060 fSegmentState = kAfterMove_SegmentState;
2061 fLastPt = fMoveTo;
2062 fNeedClose = fForceClose;
2063 break;
2064 case kLine_Verb:
2065 pts[0] = this->cons_moveTo();
2066 pts[1] = srcPts[0];
2067 fLastPt = srcPts[0];
2068 fCloseLine = false;
2069 srcPts += 1;
2070 break;
2071 case kConic_Verb:
2072 fConicWeights += 1;
2073 // fall-through
2074 case kQuad_Verb:
2075 pts[0] = this->cons_moveTo();
2076 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
2077 fLastPt = srcPts[1];
2078 srcPts += 2;
2079 break;
2080 case kCubic_Verb:
2081 pts[0] = this->cons_moveTo();
2082 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
2083 fLastPt = srcPts[2];
2084 srcPts += 3;
2085 break;
2086 case kClose_Verb:
2087 verb = this->autoClose(pts);
2088 if (verb == kLine_Verb) {
2089 fVerbs++; // move back one verb
2090 } else {
2091 fNeedClose = false;
2092 fSegmentState = kEmptyContour_SegmentState;
2093 }
2094 fLastPt = fMoveTo;
2095 break;
2096 }
2097 fPts = srcPts;
2098 return (Verb)verb;
2099 }
2100
2101 ///////////////////////////////////////////////////////////////////////////////
2102
2103 #include "include/core/SkStream.h"
2104 #include "include/core/SkString.h"
2105 #include "src/core/SkStringUtils.h"
2106
append_params(SkString * str,const char label[],const SkPoint pts[],int count,SkScalarAsStringType strType,SkScalar conicWeight=-12345)2107 static void append_params(SkString* str, const char label[], const SkPoint pts[],
2108 int count, SkScalarAsStringType strType, SkScalar conicWeight = -12345) {
2109 str->append(label);
2110 str->append("(");
2111
2112 const SkScalar* values = &pts[0].fX;
2113 count *= 2;
2114
2115 for (int i = 0; i < count; ++i) {
2116 SkAppendScalar(str, values[i], strType);
2117 if (i < count - 1) {
2118 str->append(", ");
2119 }
2120 }
2121 if (conicWeight != -12345) {
2122 str->append(", ");
2123 SkAppendScalar(str, conicWeight, strType);
2124 }
2125 str->append(");");
2126 if (kHex_SkScalarAsStringType == strType) {
2127 str->append(" // ");
2128 for (int i = 0; i < count; ++i) {
2129 SkAppendScalarDec(str, values[i]);
2130 if (i < count - 1) {
2131 str->append(", ");
2132 }
2133 }
2134 if (conicWeight >= 0) {
2135 str->append(", ");
2136 SkAppendScalarDec(str, conicWeight);
2137 }
2138 }
2139 str->append("\n");
2140 }
2141
dump(SkWStream * wStream,bool forceClose,bool dumpAsHex) const2142 void SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const {
2143 SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
2144 Iter iter(*this, forceClose);
2145 SkPoint pts[4];
2146 Verb verb;
2147
2148 SkString builder;
2149 char const * const gFillTypeStrs[] = {
2150 "Winding",
2151 "EvenOdd",
2152 "InverseWinding",
2153 "InverseEvenOdd",
2154 };
2155 builder.printf("path.setFillType(SkPath::k%s_FillType);\n",
2156 gFillTypeStrs[(int) this->getFillType()]);
2157 while ((verb = iter.next(pts)) != kDone_Verb) {
2158 switch (verb) {
2159 case kMove_Verb:
2160 append_params(&builder, "path.moveTo", &pts[0], 1, asType);
2161 break;
2162 case kLine_Verb:
2163 append_params(&builder, "path.lineTo", &pts[1], 1, asType);
2164 break;
2165 case kQuad_Verb:
2166 append_params(&builder, "path.quadTo", &pts[1], 2, asType);
2167 break;
2168 case kConic_Verb:
2169 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
2170 break;
2171 case kCubic_Verb:
2172 append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
2173 break;
2174 case kClose_Verb:
2175 builder.append("path.close();\n");
2176 break;
2177 default:
2178 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
2179 verb = kDone_Verb; // stop the loop
2180 break;
2181 }
2182 if (!wStream && builder.size()) {
2183 SkDebugf("%s", builder.c_str());
2184 builder.reset();
2185 }
2186 }
2187 if (wStream) {
2188 wStream->writeText(builder.c_str());
2189 }
2190 }
2191
dump(std::string & desc,int depth) const2192 void SkPath::dump(std::string &desc, int depth) const {
2193 std::string split(depth, '\t');
2194 desc += split + "\n SkPath:{ \n";
2195 Iter iter(*this, false);
2196 SkPoint points[4];
2197 Verb verb;
2198
2199 SkString descSk;
2200 char const * const gFillTypeStrs[] = {
2201 "Winding",
2202 "EvenOdd",
2203 "InverseWinding",
2204 "InverseEvenOdd",
2205 };
2206 descSk.printf("path.setFillType(SkPath::k%s_FillType);\n", gFillTypeStrs[(int) this->getFillType()]);
2207 while ((verb = iter.next(points)) != kDone_Verb) {
2208 switch (verb) {
2209 case kMove_Verb:
2210 append_params(&descSk, "path.moveTo", &points[0], 1, kDec_SkScalarAsStringType);
2211 break;
2212 case kLine_Verb:
2213 append_params(&descSk, "path.lineTo", &points[1], 1, kDec_SkScalarAsStringType);
2214 break;
2215 case kQuad_Verb:
2216 append_params(&descSk, "path.quadTo", &points[1], 2, kDec_SkScalarAsStringType);
2217 break;
2218 case kConic_Verb:
2219 append_params(&descSk, "path.conicTo", &points[1], 2, kDec_SkScalarAsStringType, iter.conicWeight());
2220 break;
2221 case kCubic_Verb:
2222 append_params(&descSk, "path.cubicTo", &points[1], 3, kDec_SkScalarAsStringType);
2223 break;
2224 case kClose_Verb:
2225 descSk.append("path.close();\n");
2226 break;
2227 default:
2228 verb = kDone_Verb; // stop the loop
2229 break;
2230 }
2231 if (descSk.size()) {
2232 desc += split + std::string(descSk.c_str());
2233 descSk.reset();
2234 }
2235 }
2236 desc += split + "}\n";
2237 }
2238
dump() const2239 void SkPath::dump() const {
2240 this->dump(nullptr, false, false);
2241 }
2242
dumpHex() const2243 void SkPath::dumpHex() const {
2244 this->dump(nullptr, false, true);
2245 }
2246
2247
isValidImpl() const2248 bool SkPath::isValidImpl() const {
2249 if ((fFillType & ~3) != 0) {
2250 return false;
2251 }
2252
2253 #ifdef SK_DEBUG_PATH
2254 if (!fBoundsIsDirty) {
2255 SkRect bounds;
2256
2257 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
2258 if (SkToBool(fIsFinite) != isFinite) {
2259 return false;
2260 }
2261
2262 if (fPathRef->countPoints() <= 1) {
2263 // if we're empty, fBounds may be empty but translated, so we can't
2264 // necessarily compare to bounds directly
2265 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2266 // be [2, 2, 2, 2]
2267 if (!bounds.isEmpty() || !fBounds.isEmpty()) {
2268 return false;
2269 }
2270 } else {
2271 if (bounds.isEmpty()) {
2272 if (!fBounds.isEmpty()) {
2273 return false;
2274 }
2275 } else {
2276 if (!fBounds.isEmpty()) {
2277 if (!fBounds.contains(bounds)) {
2278 return false;
2279 }
2280 }
2281 }
2282 }
2283 }
2284 #endif // SK_DEBUG_PATH
2285 return true;
2286 }
2287
2288 ///////////////////////////////////////////////////////////////////////////////
2289
2290 #ifdef SK_LEGACY_PATH_CONVEXITY // for rebaselining Chrome
2291
sign(SkScalar x)2292 static int sign(SkScalar x) { return x < 0; }
2293 #define kValueNeverReturnedBySign 2
2294
2295 enum DirChange {
2296 kLeft_DirChange,
2297 kRight_DirChange,
2298 kStraight_DirChange,
2299 kBackwards_DirChange,
2300
2301 kInvalid_DirChange
2302 };
2303
2304
almost_equal(SkScalar compA,SkScalar compB)2305 static bool almost_equal(SkScalar compA, SkScalar compB) {
2306 // The error epsilon was empirically derived; worse case round rects
2307 // with a mid point outset by 2x float epsilon in tests had an error
2308 // of 12.
2309 const int epsilon = 16;
2310 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2311 return false;
2312 }
2313 // no need to check for small numbers because SkPath::Iter has removed degenerate values
2314 int aBits = SkFloatAs2sCompliment(compA);
2315 int bBits = SkFloatAs2sCompliment(compB);
2316 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2317 }
2318
2319 // only valid for a single contour
2320 struct Convexicator {
ConvexicatorConvexicator2321 Convexicator()
2322 : fPtCount(0)
2323 , fConvexity(SkPath::kConvex_Convexity)
2324 , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
2325 , fIsFinite(true)
2326 , fIsCurve(false)
2327 , fBackwards(false) {
2328 fExpectedDir = kInvalid_DirChange;
2329 // warnings
2330 fPriorPt.set(0,0);
2331 fLastPt.set(0, 0);
2332 fCurrPt.set(0, 0);
2333 fLastVec.set(0, 0);
2334 fFirstVec.set(0, 0);
2335
2336 fDx = fDy = 0;
2337 fSx = fSy = kValueNeverReturnedBySign;
2338 }
2339
getConvexityConvexicator2340 SkPath::Convexity getConvexity() const { return fConvexity; }
2341
2342 /** The direction returned is only valid if the path is determined convex */
getFirstDirectionConvexicator2343 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
2344
addPtConvexicator2345 void addPt(const SkPoint& pt) {
2346 if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
2347 return;
2348 }
2349
2350 if (0 == fPtCount) {
2351 fCurrPt = pt;
2352 ++fPtCount;
2353 } else {
2354 SkVector vec = pt - fCurrPt;
2355 SkScalar lengthSqd = SkPointPriv::LengthSqd(vec);
2356 if (!SkScalarIsFinite(lengthSqd)) {
2357 fIsFinite = false;
2358 } else if (lengthSqd) {
2359 fPriorPt = fLastPt;
2360 fLastPt = fCurrPt;
2361 fCurrPt = pt;
2362 if (++fPtCount == 2) {
2363 fFirstVec = fLastVec = vec;
2364 } else {
2365 SkASSERT(fPtCount > 2);
2366 this->addVec(vec);
2367 }
2368
2369 int sx = sign(vec.fX);
2370 int sy = sign(vec.fY);
2371 fDx += (sx != fSx);
2372 fDy += (sy != fSy);
2373 fSx = sx;
2374 fSy = sy;
2375
2376 if (fDx > 3 || fDy > 3) {
2377 fConvexity = SkPath::kConcave_Convexity;
2378 }
2379 }
2380 }
2381 }
2382
closeConvexicator2383 void close() {
2384 if (fPtCount > 2) {
2385 this->addVec(fFirstVec);
2386 }
2387 }
2388
directionChangeConvexicator2389 DirChange directionChange(const SkVector& curVec) {
2390 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2391
2392 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2393 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2394 largest = SkTMax(largest, -smallest);
2395
2396 if (!almost_equal(largest, largest + cross)) {
2397 int sign = SkScalarSignAsInt(cross);
2398 if (sign) {
2399 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2400 }
2401 }
2402
2403 if (cross) {
2404 double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
2405 double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
2406 double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
2407 double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
2408 double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
2409 if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
2410 int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
2411 if (sign) {
2412 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2413 }
2414 }
2415 }
2416
2417 if (!SkScalarNearlyZero(SkPointPriv::LengthSqd(fLastVec),
2418 SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2419 !SkScalarNearlyZero(SkPointPriv::LengthSqd(curVec),
2420 SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2421 fLastVec.dot(curVec) < 0.0f) {
2422 return kBackwards_DirChange;
2423 }
2424
2425 return kStraight_DirChange;
2426 }
2427
hasBackwardsConvexicator2428 bool hasBackwards() const {
2429 return fBackwards;
2430 }
2431
isFiniteConvexicator2432 bool isFinite() const {
2433 return fIsFinite;
2434 }
2435
setCurveConvexicator2436 void setCurve(bool isCurve) {
2437 fIsCurve = isCurve;
2438 }
2439
2440 private:
addVecConvexicator2441 void addVec(const SkVector& vec) {
2442 SkASSERT(vec.fX || vec.fY);
2443 DirChange dir = this->directionChange(vec);
2444 switch (dir) {
2445 case kLeft_DirChange: // fall through
2446 case kRight_DirChange:
2447 if (kInvalid_DirChange == fExpectedDir) {
2448 fExpectedDir = dir;
2449 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2450 : SkPathPriv::kCCW_FirstDirection;
2451 } else if (dir != fExpectedDir) {
2452 fConvexity = SkPath::kConcave_Convexity;
2453 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2454 }
2455 fLastVec = vec;
2456 break;
2457 case kStraight_DirChange:
2458 break;
2459 case kBackwards_DirChange:
2460 if (fIsCurve) {
2461 // If any of the subsequent dir is non-backward, it'll be concave.
2462 // Otherwise, it's still convex.
2463 fExpectedDir = dir;
2464 }
2465 fLastVec = vec;
2466 fBackwards = true;
2467 break;
2468 case kInvalid_DirChange:
2469 SK_ABORT("Use of invalid direction change flag");
2470 break;
2471 }
2472 }
2473
2474 SkPoint fPriorPt;
2475 SkPoint fLastPt;
2476 SkPoint fCurrPt;
2477 // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
2478 // value with the current vec is deemed to be of a significant value.
2479 SkVector fLastVec, fFirstVec;
2480 int fPtCount; // non-degenerate points
2481 DirChange fExpectedDir;
2482 SkPath::Convexity fConvexity;
2483 SkPathPriv::FirstDirection fFirstDirection;
2484 int fDx, fDy, fSx, fSy;
2485 bool fIsFinite;
2486 bool fIsCurve;
2487 bool fBackwards;
2488 };
2489
internalGetConvexity() const2490 SkPath::Convexity SkPath::internalGetConvexity() const {
2491 // Sometimes we think we need to calculate convexity but another thread already did.
2492 auto c = this->getConvexityOrUnknown();
2493 if (c != kUnknown_Convexity) {
2494 return c;
2495 }
2496
2497 SkPoint pts[4];
2498 SkPath::Verb verb;
2499 SkPath::Iter iter(*this, true);
2500
2501 int contourCount = 0;
2502 int count;
2503 Convexicator state;
2504
2505 if (!isFinite()) {
2506 return kUnknown_Convexity;
2507 }
2508 while ((verb = iter.next(pts, false, false)) != SkPath::kDone_Verb) {
2509 switch (verb) {
2510 case kMove_Verb:
2511 if (++contourCount > 1) {
2512 this->setConvexity(kConcave_Convexity);
2513 return kConcave_Convexity;
2514 }
2515 pts[1] = pts[0];
2516 // fall through
2517 case kLine_Verb:
2518 count = 1;
2519 state.setCurve(false);
2520 break;
2521 case kQuad_Verb:
2522 // fall through
2523 case kConic_Verb:
2524 // fall through
2525 case kCubic_Verb:
2526 count = 2 + (kCubic_Verb == verb);
2527 // As an additional enhancement, this could set curve true only
2528 // if the curve is nonlinear
2529 state.setCurve(true);
2530 break;
2531 case kClose_Verb:
2532 state.setCurve(false);
2533 state.close();
2534 count = 0;
2535 break;
2536 default:
2537 SkDEBUGFAIL("bad verb");
2538 this->setConvexity(kConcave_Convexity);
2539 return kConcave_Convexity;
2540 }
2541
2542 for (int i = 1; i <= count; i++) {
2543 state.addPt(pts[i]);
2544 }
2545 // early exit
2546 if (!state.isFinite()) {
2547 return kUnknown_Convexity;
2548 }
2549 if (kConcave_Convexity == state.getConvexity()) {
2550 this->setConvexity(kConcave_Convexity);
2551 return kConcave_Convexity;
2552 }
2553 }
2554 this->setConvexity(state.getConvexity());
2555
2556 if (this->getConvexityOrUnknown() == kConvex_Convexity &&
2557 this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
2558
2559 if (state.getFirstDirection() == SkPathPriv::kUnknown_FirstDirection
2560 && !this->getBounds().isEmpty()
2561 && !state.hasBackwards()) {
2562 this->setConvexity(Convexity::kConcave_Convexity);
2563 } else {
2564 this->setFirstDirection(state.getFirstDirection());
2565 }
2566 }
2567 return this->getConvexityOrUnknown();
2568 }
2569
2570 #else
2571
sign(SkScalar x)2572 static int sign(SkScalar x) { return x < 0; }
2573 #define kValueNeverReturnedBySign 2
2574
2575 enum DirChange {
2576 kUnknown_DirChange,
2577 kLeft_DirChange,
2578 kRight_DirChange,
2579 kStraight_DirChange,
2580 kBackwards_DirChange, // if double back, allow simple lines to be convex
2581 kInvalid_DirChange
2582 };
2583
2584
almost_equal(SkScalar compA,SkScalar compB)2585 static bool almost_equal(SkScalar compA, SkScalar compB) {
2586 // The error epsilon was empirically derived; worse case round rects
2587 // with a mid point outset by 2x float epsilon in tests had an error
2588 // of 12.
2589 const int epsilon = 16;
2590 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2591 return false;
2592 }
2593 // no need to check for small numbers because SkPath::Iter has removed degenerate values
2594 int aBits = SkFloatAs2sCompliment(compA);
2595 int bBits = SkFloatAs2sCompliment(compB);
2596 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2597 }
2598
2599 // only valid for a single contour
2600 struct Convexicator {
2601
2602 /** The direction returned is only valid if the path is determined convex */
getFirstDirectionConvexicator2603 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
2604
setMovePtConvexicator2605 void setMovePt(const SkPoint& pt) {
2606 fPriorPt = fLastPt = fCurrPt = pt;
2607 }
2608
addPtConvexicator2609 bool addPt(const SkPoint& pt) {
2610 if (fCurrPt == pt) {
2611 return true;
2612 }
2613 fCurrPt = pt;
2614 if (fPriorPt == fLastPt) { // should only be true for first non-zero vector
2615 fLastVec = fCurrPt - fLastPt;
2616 fFirstPt = pt;
2617 } else if (!this->addVec(fCurrPt - fLastPt)) {
2618 return false;
2619 }
2620 fPriorPt = fLastPt;
2621 fLastPt = fCurrPt;
2622 return true;
2623 }
2624
BySignConvexicator2625 static SkPath::Convexity BySign(const SkPoint points[], int count) {
2626 const SkPoint* last = points + count;
2627 SkPoint currPt = *points++;
2628 SkPoint firstPt = currPt;
2629 int dxes = 0;
2630 int dyes = 0;
2631 int lastSx = kValueNeverReturnedBySign;
2632 int lastSy = kValueNeverReturnedBySign;
2633 for (int outerLoop = 0; outerLoop < 2; ++outerLoop ) {
2634 while (points != last) {
2635 SkVector vec = *points - currPt;
2636 if (!vec.isZero()) {
2637 // give up if vector construction failed
2638 if (!vec.isFinite()) {
2639 return SkPath::kUnknown_Convexity;
2640 }
2641 int sx = sign(vec.fX);
2642 int sy = sign(vec.fY);
2643 dxes += (sx != lastSx);
2644 dyes += (sy != lastSy);
2645 if (dxes > 3 || dyes > 3) {
2646 return SkPath::kConcave_Convexity;
2647 }
2648 lastSx = sx;
2649 lastSy = sy;
2650 }
2651 currPt = *points++;
2652 if (outerLoop) {
2653 break;
2654 }
2655 }
2656 points = &firstPt;
2657 }
2658 return SkPath::kConvex_Convexity; // that is, it may be convex, don't know yet
2659 }
2660
closeConvexicator2661 bool close() {
2662 return this->addPt(fFirstPt);
2663 }
2664
isFiniteConvexicator2665 bool isFinite() const {
2666 return fIsFinite;
2667 }
2668
reversalsConvexicator2669 int reversals() const {
2670 return fReversals;
2671 }
2672
2673 private:
directionChangeConvexicator2674 DirChange directionChange(const SkVector& curVec) {
2675 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2676 if (!SkScalarIsFinite(cross)) {
2677 return kUnknown_DirChange;
2678 }
2679 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2680 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2681 largest = SkTMax(largest, -smallest);
2682
2683 if (almost_equal(largest, largest + cross)) {
2684 return fLastVec.dot(curVec) < 0 ? kBackwards_DirChange : kStraight_DirChange;
2685 }
2686 return 1 == SkScalarSignAsInt(cross) ? kRight_DirChange : kLeft_DirChange;
2687 }
2688
addVecConvexicator2689 bool addVec(const SkVector& curVec) {
2690 DirChange dir = this->directionChange(curVec);
2691 switch (dir) {
2692 case kLeft_DirChange: // fall through
2693 case kRight_DirChange:
2694 if (kInvalid_DirChange == fExpectedDir) {
2695 fExpectedDir = dir;
2696 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2697 : SkPathPriv::kCCW_FirstDirection;
2698 } else if (dir != fExpectedDir) {
2699 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2700 return false;
2701 }
2702 fLastVec = curVec;
2703 break;
2704 case kStraight_DirChange:
2705 break;
2706 case kBackwards_DirChange:
2707 // allow path to reverse direction twice
2708 // Given path.moveTo(0, 0); path.lineTo(1, 1);
2709 // - 1st reversal: direction change formed by line (0,0 1,1), line (1,1 0,0)
2710 // - 2nd reversal: direction change formed by line (1,1 0,0), line (0,0 1,1)
2711 fLastVec = curVec;
2712 return ++fReversals < 3;
2713 case kUnknown_DirChange:
2714 return (fIsFinite = false);
2715 case kInvalid_DirChange:
2716 SK_ABORT("Use of invalid direction change flag");
2717 break;
2718 }
2719 return true;
2720 }
2721
2722 SkPoint fFirstPt {0, 0};
2723 SkPoint fPriorPt {0, 0};
2724 SkPoint fLastPt {0, 0};
2725 SkPoint fCurrPt {0, 0};
2726 SkVector fLastVec {0, 0};
2727 DirChange fExpectedDir { kInvalid_DirChange };
2728 SkPathPriv::FirstDirection fFirstDirection { SkPathPriv::kUnknown_FirstDirection };
2729 int fReversals { 0 };
2730 bool fIsFinite { true };
2731 };
2732
internalGetConvexity() const2733 SkPath::Convexity SkPath::internalGetConvexity() const {
2734 SkPoint pts[4];
2735 SkPath::Verb verb;
2736 SkPath::Iter iter(*this, true);
2737 auto setComputedConvexity = [=](Convexity convexity){
2738 SkASSERT(kUnknown_Convexity != convexity);
2739 this->setConvexity(convexity);
2740 return convexity;
2741 };
2742
2743 // Check to see if path changes direction more than three times as quick concave test
2744 int pointCount = this->countPoints();
2745 // last moveTo index may exceed point count if data comes from fuzzer (via SkImageFilter)
2746 if (0 < fLastMoveToIndex && fLastMoveToIndex < pointCount) {
2747 pointCount = fLastMoveToIndex;
2748 }
2749 if (pointCount > 3) {
2750 const SkPoint* points = fPathRef->points();
2751 const SkPoint* last = &points[pointCount];
2752 // only consider the last of the initial move tos
2753 while (SkPath::kMove_Verb == iter.next(pts)) {
2754 ++points;
2755 }
2756 --points;
2757 SkPath::Convexity convexity = Convexicator::BySign(points, (int) (last - points));
2758 if (SkPath::kConcave_Convexity == convexity) {
2759 return setComputedConvexity(SkPath::kConcave_Convexity);
2760 } else if (SkPath::kUnknown_Convexity == convexity) {
2761 return SkPath::kUnknown_Convexity;
2762 }
2763 iter.setPath(*this, true);
2764 } else if (!this->isFinite()) {
2765 return kUnknown_Convexity;
2766 }
2767
2768 int contourCount = 0;
2769 int count;
2770 Convexicator state;
2771 auto setFail = [=](){
2772 if (!state.isFinite()) {
2773 return SkPath::kUnknown_Convexity;
2774 }
2775 return setComputedConvexity(SkPath::kConcave_Convexity);
2776 };
2777
2778 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
2779 switch (verb) {
2780 case kMove_Verb:
2781 if (++contourCount > 1) {
2782 return setComputedConvexity(kConcave_Convexity);
2783 }
2784 state.setMovePt(pts[0]);
2785 count = 0;
2786 break;
2787 case kLine_Verb:
2788 count = 1;
2789 break;
2790 case kQuad_Verb:
2791 // fall through
2792 case kConic_Verb:
2793 count = 2;
2794 break;
2795 case kCubic_Verb:
2796 count = 3;
2797 break;
2798 case kClose_Verb:
2799 if (!state.close()) {
2800 return setFail();
2801 }
2802 count = 0;
2803 break;
2804 default:
2805 SkDEBUGFAIL("bad verb");
2806 return setComputedConvexity(kConcave_Convexity);
2807 }
2808 for (int i = 1; i <= count; i++) {
2809 if (!state.addPt(pts[i])) {
2810 return setFail();
2811 }
2812 }
2813 }
2814
2815 if (this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
2816 if (state.getFirstDirection() == SkPathPriv::kUnknown_FirstDirection
2817 && !this->getBounds().isEmpty()) {
2818 return setComputedConvexity(state.reversals() < 3 ?
2819 kConvex_Convexity : kConcave_Convexity);
2820 }
2821 this->setFirstDirection(state.getFirstDirection());
2822 }
2823 return setComputedConvexity(kConvex_Convexity);
2824 }
2825
IsConvex(const SkPoint points[],int count)2826 bool SkPathPriv::IsConvex(const SkPoint points[], int count) {
2827 SkPath::Convexity convexity = Convexicator::BySign(points, count);
2828 if (SkPath::kConvex_Convexity != convexity) {
2829 return false;
2830 }
2831 Convexicator state;
2832 state.setMovePt(points[0]);
2833 for (int i = 1; i < count; i++) {
2834 if (!state.addPt(points[i])) {
2835 return false;
2836 }
2837 }
2838 if (!state.addPt(points[0])) {
2839 return false;
2840 }
2841 if (!state.close()) {
2842 return false;
2843 }
2844 return state.getFirstDirection() != SkPathPriv::kUnknown_FirstDirection
2845 || state.reversals() < 3;
2846 }
2847
2848 #endif
2849
2850 ///////////////////////////////////////////////////////////////////////////////
2851
2852 class ContourIter {
2853 public:
2854 ContourIter(const SkPathRef& pathRef);
2855
done() const2856 bool done() const { return fDone; }
2857 // if !done() then these may be called
count() const2858 int count() const { return fCurrPtCount; }
pts() const2859 const SkPoint* pts() const { return fCurrPt; }
2860 void next();
2861
2862 private:
2863 int fCurrPtCount;
2864 const SkPoint* fCurrPt;
2865 const uint8_t* fCurrVerb;
2866 const uint8_t* fStopVerbs;
2867 const SkScalar* fCurrConicWeight;
2868 bool fDone;
2869 SkDEBUGCODE(int fContourCounter;)
2870 };
2871
ContourIter(const SkPathRef & pathRef)2872 ContourIter::ContourIter(const SkPathRef& pathRef) {
2873 fStopVerbs = pathRef.verbsMemBegin();
2874 fDone = false;
2875 fCurrPt = pathRef.points();
2876 fCurrVerb = pathRef.verbs();
2877 fCurrConicWeight = pathRef.conicWeights();
2878 fCurrPtCount = 0;
2879 SkDEBUGCODE(fContourCounter = 0;)
2880 this->next();
2881 }
2882
next()2883 void ContourIter::next() {
2884 if (fCurrVerb <= fStopVerbs) {
2885 fDone = true;
2886 }
2887 if (fDone) {
2888 return;
2889 }
2890
2891 // skip pts of prev contour
2892 fCurrPt += fCurrPtCount;
2893
2894 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
2895 int ptCount = 1; // moveTo
2896 const uint8_t* verbs = fCurrVerb;
2897
2898 for (--verbs; verbs > fStopVerbs; --verbs) {
2899 switch (verbs[~0]) {
2900 case SkPath::kMove_Verb:
2901 goto CONTOUR_END;
2902 case SkPath::kLine_Verb:
2903 ptCount += 1;
2904 break;
2905 case SkPath::kConic_Verb:
2906 fCurrConicWeight += 1;
2907 // fall-through
2908 case SkPath::kQuad_Verb:
2909 ptCount += 2;
2910 break;
2911 case SkPath::kCubic_Verb:
2912 ptCount += 3;
2913 break;
2914 case SkPath::kClose_Verb:
2915 break;
2916 default:
2917 SkDEBUGFAIL("unexpected verb");
2918 break;
2919 }
2920 }
2921 CONTOUR_END:
2922 fCurrPtCount = ptCount;
2923 fCurrVerb = verbs;
2924 SkDEBUGCODE(++fContourCounter;)
2925 }
2926
2927 // returns cross product of (p1 - p0) and (p2 - p0)
cross_prod(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2)2928 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2929 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2930 // We may get 0 when the above subtracts underflow. We expect this to be
2931 // very rare and lazily promote to double.
2932 if (0 == cross) {
2933 double p0x = SkScalarToDouble(p0.fX);
2934 double p0y = SkScalarToDouble(p0.fY);
2935
2936 double p1x = SkScalarToDouble(p1.fX);
2937 double p1y = SkScalarToDouble(p1.fY);
2938
2939 double p2x = SkScalarToDouble(p2.fX);
2940 double p2y = SkScalarToDouble(p2.fY);
2941
2942 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
2943 (p1y - p0y) * (p2x - p0x));
2944
2945 }
2946 return cross;
2947 }
2948
2949 // Returns the first pt with the maximum Y coordinate
find_max_y(const SkPoint pts[],int count)2950 static int find_max_y(const SkPoint pts[], int count) {
2951 SkASSERT(count > 0);
2952 SkScalar max = pts[0].fY;
2953 int firstIndex = 0;
2954 for (int i = 1; i < count; ++i) {
2955 SkScalar y = pts[i].fY;
2956 if (y > max) {
2957 max = y;
2958 firstIndex = i;
2959 }
2960 }
2961 return firstIndex;
2962 }
2963
find_diff_pt(const SkPoint pts[],int index,int n,int inc)2964 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
2965 int i = index;
2966 for (;;) {
2967 i = (i + inc) % n;
2968 if (i == index) { // we wrapped around, so abort
2969 break;
2970 }
2971 if (pts[index] != pts[i]) { // found a different point, success!
2972 break;
2973 }
2974 }
2975 return i;
2976 }
2977
2978 /**
2979 * Starting at index, and moving forward (incrementing), find the xmin and
2980 * xmax of the contiguous points that have the same Y.
2981 */
find_min_max_x_at_y(const SkPoint pts[],int index,int n,int * maxIndexPtr)2982 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
2983 int* maxIndexPtr) {
2984 const SkScalar y = pts[index].fY;
2985 SkScalar min = pts[index].fX;
2986 SkScalar max = min;
2987 int minIndex = index;
2988 int maxIndex = index;
2989 for (int i = index + 1; i < n; ++i) {
2990 if (pts[i].fY != y) {
2991 break;
2992 }
2993 SkScalar x = pts[i].fX;
2994 if (x < min) {
2995 min = x;
2996 minIndex = i;
2997 } else if (x > max) {
2998 max = x;
2999 maxIndex = i;
3000 }
3001 }
3002 *maxIndexPtr = maxIndex;
3003 return minIndex;
3004 }
3005
crossToDir(SkScalar cross,SkPathPriv::FirstDirection * dir)3006 static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
3007 *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
3008 }
3009
3010 /*
3011 * We loop through all contours, and keep the computed cross-product of the
3012 * contour that contained the global y-max. If we just look at the first
3013 * contour, we may find one that is wound the opposite way (correctly) since
3014 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
3015 * that is outer most (or at least has the global y-max) before we can consider
3016 * its cross product.
3017 */
CheapComputeFirstDirection(const SkPath & path,FirstDirection * dir)3018 bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
3019 auto d = path.getFirstDirection();
3020 if (d != kUnknown_FirstDirection) {
3021 *dir = static_cast<FirstDirection>(d);
3022 return true;
3023 }
3024
3025 // We don't want to pay the cost for computing convexity if it is unknown,
3026 // so we call getConvexityOrUnknown() instead of isConvex().
3027 if (path.getConvexityOrUnknown() == SkPath::kConvex_Convexity) {
3028 SkASSERT(path.getFirstDirection() == kUnknown_FirstDirection);
3029 *dir = static_cast<FirstDirection>(path.getFirstDirection());
3030 return false;
3031 }
3032
3033 ContourIter iter(*path.fPathRef.get());
3034
3035 // initialize with our logical y-min
3036 SkScalar ymax = path.getBounds().fTop;
3037 SkScalar ymaxCross = 0;
3038
3039 for (; !iter.done(); iter.next()) {
3040 int n = iter.count();
3041 if (n < 3) {
3042 continue;
3043 }
3044
3045 const SkPoint* pts = iter.pts();
3046 SkScalar cross = 0;
3047 int index = find_max_y(pts, n);
3048 if (pts[index].fY < ymax) {
3049 continue;
3050 }
3051
3052 // If there is more than 1 distinct point at the y-max, we take the
3053 // x-min and x-max of them and just subtract to compute the dir.
3054 if (pts[(index + 1) % n].fY == pts[index].fY) {
3055 int maxIndex;
3056 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
3057 if (minIndex == maxIndex) {
3058 goto TRY_CROSSPROD;
3059 }
3060 SkASSERT(pts[minIndex].fY == pts[index].fY);
3061 SkASSERT(pts[maxIndex].fY == pts[index].fY);
3062 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
3063 // we just subtract the indices, and let that auto-convert to
3064 // SkScalar, since we just want - or + to signal the direction.
3065 cross = minIndex - maxIndex;
3066 } else {
3067 TRY_CROSSPROD:
3068 // Find a next and prev index to use for the cross-product test,
3069 // but we try to find pts that form non-zero vectors from pts[index]
3070 //
3071 // Its possible that we can't find two non-degenerate vectors, so
3072 // we have to guard our search (e.g. all the pts could be in the
3073 // same place).
3074
3075 // we pass n - 1 instead of -1 so we don't foul up % operator by
3076 // passing it a negative LH argument.
3077 int prev = find_diff_pt(pts, index, n, n - 1);
3078 if (prev == index) {
3079 // completely degenerate, skip to next contour
3080 continue;
3081 }
3082 int next = find_diff_pt(pts, index, n, 1);
3083 SkASSERT(next != index);
3084 cross = cross_prod(pts[prev], pts[index], pts[next]);
3085 // if we get a zero and the points are horizontal, then we look at the spread in
3086 // x-direction. We really should continue to walk away from the degeneracy until
3087 // there is a divergence.
3088 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
3089 // construct the subtract so we get the correct Direction below
3090 cross = pts[index].fX - pts[next].fX;
3091 }
3092 }
3093
3094 if (cross) {
3095 // record our best guess so far
3096 ymax = pts[index].fY;
3097 ymaxCross = cross;
3098 }
3099 }
3100 if (ymaxCross) {
3101 crossToDir(ymaxCross, dir);
3102 path.setFirstDirection(*dir);
3103 return true;
3104 } else {
3105 return false;
3106 }
3107 }
3108
3109 ///////////////////////////////////////////////////////////////////////////////
3110
between(SkScalar a,SkScalar b,SkScalar c)3111 static bool between(SkScalar a, SkScalar b, SkScalar c) {
3112 SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
3113 || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
3114 return (a - b) * (c - b) <= 0;
3115 }
3116
eval_cubic_pts(SkScalar c0,SkScalar c1,SkScalar c2,SkScalar c3,SkScalar t)3117 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
3118 SkScalar t) {
3119 SkScalar A = c3 + 3*(c1 - c2) - c0;
3120 SkScalar B = 3*(c2 - c1 - c1 + c0);
3121 SkScalar C = 3*(c1 - c0);
3122 SkScalar D = c0;
3123 return poly_eval(A, B, C, D, t);
3124 }
3125
find_minmax(const SkPoint pts[],SkScalar * minPtr,SkScalar * maxPtr)3126 template <size_t N> static void find_minmax(const SkPoint pts[],
3127 SkScalar* minPtr, SkScalar* maxPtr) {
3128 SkScalar min, max;
3129 min = max = pts[0].fX;
3130 for (size_t i = 1; i < N; ++i) {
3131 min = SkMinScalar(min, pts[i].fX);
3132 max = SkMaxScalar(max, pts[i].fX);
3133 }
3134 *minPtr = min;
3135 *maxPtr = max;
3136 }
3137
checkOnCurve(SkScalar x,SkScalar y,const SkPoint & start,const SkPoint & end)3138 static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
3139 if (start.fY == end.fY) {
3140 return between(start.fX, x, end.fX) && x != end.fX;
3141 } else {
3142 return x == start.fX && y == start.fY;
3143 }
3144 }
3145
winding_mono_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3146 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3147 SkScalar y0 = pts[0].fY;
3148 SkScalar y3 = pts[3].fY;
3149
3150 int dir = 1;
3151 if (y0 > y3) {
3152 using std::swap;
3153 swap(y0, y3);
3154 dir = -1;
3155 }
3156 if (y < y0 || y > y3) {
3157 return 0;
3158 }
3159 if (checkOnCurve(x, y, pts[0], pts[3])) {
3160 *onCurveCount += 1;
3161 return 0;
3162 }
3163 if (y == y3) {
3164 return 0;
3165 }
3166
3167 // quickreject or quickaccept
3168 SkScalar min, max;
3169 find_minmax<4>(pts, &min, &max);
3170 if (x < min) {
3171 return 0;
3172 }
3173 if (x > max) {
3174 return dir;
3175 }
3176
3177 // compute the actual x(t) value
3178 SkScalar t;
3179 if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
3180 return 0;
3181 }
3182 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
3183 if (SkScalarNearlyEqual(xt, x)) {
3184 if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points
3185 *onCurveCount += 1;
3186 return 0;
3187 }
3188 }
3189 return xt < x ? dir : 0;
3190 }
3191
winding_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3192 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3193 SkPoint dst[10];
3194 int n = SkChopCubicAtYExtrema(pts, dst);
3195 int w = 0;
3196 for (int i = 0; i <= n; ++i) {
3197 w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
3198 }
3199 return w;
3200 }
3201
conic_eval_numerator(const SkScalar src[],SkScalar w,SkScalar t)3202 static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
3203 SkASSERT(src);
3204 SkASSERT(t >= 0 && t <= 1);
3205 SkScalar src2w = src[2] * w;
3206 SkScalar C = src[0];
3207 SkScalar A = src[4] - 2 * src2w + C;
3208 SkScalar B = 2 * (src2w - C);
3209 return poly_eval(A, B, C, t);
3210 }
3211
3212
conic_eval_denominator(SkScalar w,SkScalar t)3213 static double conic_eval_denominator(SkScalar w, SkScalar t) {
3214 SkScalar B = 2 * (w - 1);
3215 SkScalar C = 1;
3216 SkScalar A = -B;
3217 return poly_eval(A, B, C, t);
3218 }
3219
winding_mono_conic(const SkConic & conic,SkScalar x,SkScalar y,int * onCurveCount)3220 static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
3221 const SkPoint* pts = conic.fPts;
3222 SkScalar y0 = pts[0].fY;
3223 SkScalar y2 = pts[2].fY;
3224
3225 int dir = 1;
3226 if (y0 > y2) {
3227 using std::swap;
3228 swap(y0, y2);
3229 dir = -1;
3230 }
3231 if (y < y0 || y > y2) {
3232 return 0;
3233 }
3234 if (checkOnCurve(x, y, pts[0], pts[2])) {
3235 *onCurveCount += 1;
3236 return 0;
3237 }
3238 if (y == y2) {
3239 return 0;
3240 }
3241
3242 SkScalar roots[2];
3243 SkScalar A = pts[2].fY;
3244 SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
3245 SkScalar C = pts[0].fY;
3246 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
3247 B -= C; // B = b*w - w * yCept + yCept - a
3248 C -= y;
3249 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3250 SkASSERT(n <= 1);
3251 SkScalar xt;
3252 if (0 == n) {
3253 // zero roots are returned only when y0 == y
3254 // Need [0] if dir == 1
3255 // and [2] if dir == -1
3256 xt = pts[1 - dir].fX;
3257 } else {
3258 SkScalar t = roots[0];
3259 xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
3260 }
3261 if (SkScalarNearlyEqual(xt, x)) {
3262 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
3263 *onCurveCount += 1;
3264 return 0;
3265 }
3266 }
3267 return xt < x ? dir : 0;
3268 }
3269
is_mono_quad(SkScalar y0,SkScalar y1,SkScalar y2)3270 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
3271 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
3272 if (y0 == y1) {
3273 return true;
3274 }
3275 if (y0 < y1) {
3276 return y1 <= y2;
3277 } else {
3278 return y1 >= y2;
3279 }
3280 }
3281
winding_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar weight,int * onCurveCount)3282 static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
3283 int* onCurveCount) {
3284 SkConic conic(pts, weight);
3285 SkConic chopped[2];
3286 // If the data points are very large, the conic may not be monotonic but may also
3287 // fail to chop. Then, the chopper does not split the original conic in two.
3288 bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
3289 int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
3290 if (!isMono) {
3291 w += winding_mono_conic(chopped[1], x, y, onCurveCount);
3292 }
3293 return w;
3294 }
3295
winding_mono_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3296 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3297 SkScalar y0 = pts[0].fY;
3298 SkScalar y2 = pts[2].fY;
3299
3300 int dir = 1;
3301 if (y0 > y2) {
3302 using std::swap;
3303 swap(y0, y2);
3304 dir = -1;
3305 }
3306 if (y < y0 || y > y2) {
3307 return 0;
3308 }
3309 if (checkOnCurve(x, y, pts[0], pts[2])) {
3310 *onCurveCount += 1;
3311 return 0;
3312 }
3313 if (y == y2) {
3314 return 0;
3315 }
3316 // bounds check on X (not required. is it faster?)
3317 #if 0
3318 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
3319 return 0;
3320 }
3321 #endif
3322
3323 SkScalar roots[2];
3324 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3325 2 * (pts[1].fY - pts[0].fY),
3326 pts[0].fY - y,
3327 roots);
3328 SkASSERT(n <= 1);
3329 SkScalar xt;
3330 if (0 == n) {
3331 // zero roots are returned only when y0 == y
3332 // Need [0] if dir == 1
3333 // and [2] if dir == -1
3334 xt = pts[1 - dir].fX;
3335 } else {
3336 SkScalar t = roots[0];
3337 SkScalar C = pts[0].fX;
3338 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3339 SkScalar B = 2 * (pts[1].fX - C);
3340 xt = poly_eval(A, B, C, t);
3341 }
3342 if (SkScalarNearlyEqual(xt, x)) {
3343 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
3344 *onCurveCount += 1;
3345 return 0;
3346 }
3347 }
3348 return xt < x ? dir : 0;
3349 }
3350
winding_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3351 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3352 SkPoint dst[5];
3353 int n = 0;
3354
3355 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
3356 n = SkChopQuadAtYExtrema(pts, dst);
3357 pts = dst;
3358 }
3359 int w = winding_mono_quad(pts, x, y, onCurveCount);
3360 if (n > 0) {
3361 w += winding_mono_quad(&pts[2], x, y, onCurveCount);
3362 }
3363 return w;
3364 }
3365
winding_line(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)3366 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3367 SkScalar x0 = pts[0].fX;
3368 SkScalar y0 = pts[0].fY;
3369 SkScalar x1 = pts[1].fX;
3370 SkScalar y1 = pts[1].fY;
3371
3372 SkScalar dy = y1 - y0;
3373
3374 int dir = 1;
3375 if (y0 > y1) {
3376 using std::swap;
3377 swap(y0, y1);
3378 dir = -1;
3379 }
3380 if (y < y0 || y > y1) {
3381 return 0;
3382 }
3383 if (checkOnCurve(x, y, pts[0], pts[1])) {
3384 *onCurveCount += 1;
3385 return 0;
3386 }
3387 if (y == y1) {
3388 return 0;
3389 }
3390 SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0);
3391
3392 if (!cross) {
3393 // zero cross means the point is on the line, and since the case where
3394 // y of the query point is at the end point is handled above, we can be
3395 // sure that we're on the line (excluding the end point) here
3396 if (x != x1 || y != pts[1].fY) {
3397 *onCurveCount += 1;
3398 }
3399 dir = 0;
3400 } else if (SkScalarSignAsInt(cross) == dir) {
3401 dir = 0;
3402 }
3403 return dir;
3404 }
3405
tangent_cubic(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3406 static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
3407 SkTDArray<SkVector>* tangents) {
3408 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
3409 && !between(pts[2].fY, y, pts[3].fY)) {
3410 return;
3411 }
3412 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
3413 && !between(pts[2].fX, x, pts[3].fX)) {
3414 return;
3415 }
3416 SkPoint dst[10];
3417 int n = SkChopCubicAtYExtrema(pts, dst);
3418 for (int i = 0; i <= n; ++i) {
3419 SkPoint* c = &dst[i * 3];
3420 SkScalar t;
3421 if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
3422 continue;
3423 }
3424 SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
3425 if (!SkScalarNearlyEqual(x, xt)) {
3426 continue;
3427 }
3428 SkVector tangent;
3429 SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
3430 tangents->push_back(tangent);
3431 }
3432 }
3433
tangent_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar w,SkTDArray<SkVector> * tangents)3434 static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
3435 SkTDArray<SkVector>* tangents) {
3436 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3437 return;
3438 }
3439 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3440 return;
3441 }
3442 SkScalar roots[2];
3443 SkScalar A = pts[2].fY;
3444 SkScalar B = pts[1].fY * w - y * w + y;
3445 SkScalar C = pts[0].fY;
3446 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
3447 B -= C; // B = b*w - w * yCept + yCept - a
3448 C -= y;
3449 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3450 for (int index = 0; index < n; ++index) {
3451 SkScalar t = roots[index];
3452 SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
3453 if (!SkScalarNearlyEqual(x, xt)) {
3454 continue;
3455 }
3456 SkConic conic(pts, w);
3457 tangents->push_back(conic.evalTangentAt(t));
3458 }
3459 }
3460
tangent_quad(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3461 static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
3462 SkTDArray<SkVector>* tangents) {
3463 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3464 return;
3465 }
3466 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3467 return;
3468 }
3469 SkScalar roots[2];
3470 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3471 2 * (pts[1].fY - pts[0].fY),
3472 pts[0].fY - y,
3473 roots);
3474 for (int index = 0; index < n; ++index) {
3475 SkScalar t = roots[index];
3476 SkScalar C = pts[0].fX;
3477 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3478 SkScalar B = 2 * (pts[1].fX - C);
3479 SkScalar xt = poly_eval(A, B, C, t);
3480 if (!SkScalarNearlyEqual(x, xt)) {
3481 continue;
3482 }
3483 tangents->push_back(SkEvalQuadTangentAt(pts, t));
3484 }
3485 }
3486
tangent_line(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3487 static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
3488 SkTDArray<SkVector>* tangents) {
3489 SkScalar y0 = pts[0].fY;
3490 SkScalar y1 = pts[1].fY;
3491 if (!between(y0, y, y1)) {
3492 return;
3493 }
3494 SkScalar x0 = pts[0].fX;
3495 SkScalar x1 = pts[1].fX;
3496 if (!between(x0, x, x1)) {
3497 return;
3498 }
3499 SkScalar dx = x1 - x0;
3500 SkScalar dy = y1 - y0;
3501 if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
3502 return;
3503 }
3504 SkVector v;
3505 v.set(dx, dy);
3506 tangents->push_back(v);
3507 }
3508
contains_inclusive(const SkRect & r,SkScalar x,SkScalar y)3509 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3510 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3511 }
3512
contains(SkScalar x,SkScalar y) const3513 bool SkPath::contains(SkScalar x, SkScalar y) const {
3514 bool isInverse = this->isInverseFillType();
3515 if (this->isEmpty()) {
3516 return isInverse;
3517 }
3518
3519 if (!contains_inclusive(this->getBounds(), x, y)) {
3520 return isInverse;
3521 }
3522
3523 SkPath::Iter iter(*this, true);
3524 bool done = false;
3525 int w = 0;
3526 int onCurveCount = 0;
3527 do {
3528 SkPoint pts[4];
3529 switch (iter.next(pts)) {
3530 case SkPath::kMove_Verb:
3531 case SkPath::kClose_Verb:
3532 break;
3533 case SkPath::kLine_Verb:
3534 w += winding_line(pts, x, y, &onCurveCount);
3535 break;
3536 case SkPath::kQuad_Verb:
3537 w += winding_quad(pts, x, y, &onCurveCount);
3538 break;
3539 case SkPath::kConic_Verb:
3540 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3541 break;
3542 case SkPath::kCubic_Verb:
3543 w += winding_cubic(pts, x, y, &onCurveCount);
3544 break;
3545 case SkPath::kDone_Verb:
3546 done = true;
3547 break;
3548 }
3549 } while (!done);
3550 bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
3551 || SkPath::kInverseEvenOdd_FillType == this->getFillType();
3552 if (evenOddFill) {
3553 w &= 1;
3554 }
3555 if (w) {
3556 return !isInverse;
3557 }
3558 if (onCurveCount <= 1) {
3559 return SkToBool(onCurveCount) ^ isInverse;
3560 }
3561 if ((onCurveCount & 1) || evenOddFill) {
3562 return SkToBool(onCurveCount & 1) ^ isInverse;
3563 }
3564 // If the point touches an even number of curves, and the fill is winding, check for
3565 // coincidence. Count coincidence as places where the on curve points have identical tangents.
3566 iter.setPath(*this, true);
3567 done = false;
3568 SkTDArray<SkVector> tangents;
3569 do {
3570 SkPoint pts[4];
3571 int oldCount = tangents.count();
3572 switch (iter.next(pts)) {
3573 case SkPath::kMove_Verb:
3574 case SkPath::kClose_Verb:
3575 break;
3576 case SkPath::kLine_Verb:
3577 tangent_line(pts, x, y, &tangents);
3578 break;
3579 case SkPath::kQuad_Verb:
3580 tangent_quad(pts, x, y, &tangents);
3581 break;
3582 case SkPath::kConic_Verb:
3583 tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
3584 break;
3585 case SkPath::kCubic_Verb:
3586 tangent_cubic(pts, x, y, &tangents);
3587 break;
3588 case SkPath::kDone_Verb:
3589 done = true;
3590 break;
3591 }
3592 if (tangents.count() > oldCount) {
3593 int last = tangents.count() - 1;
3594 const SkVector& tangent = tangents[last];
3595 if (SkScalarNearlyZero(SkPointPriv::LengthSqd(tangent))) {
3596 tangents.remove(last);
3597 } else {
3598 for (int index = 0; index < last; ++index) {
3599 const SkVector& test = tangents[index];
3600 if (SkScalarNearlyZero(test.cross(tangent))
3601 && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
3602 && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
3603 tangents.remove(last);
3604 tangents.removeShuffle(index);
3605 break;
3606 }
3607 }
3608 }
3609 }
3610 } while (!done);
3611 return SkToBool(tangents.count()) ^ isInverse;
3612 }
3613
ConvertConicToQuads(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,SkScalar w,SkPoint pts[],int pow2)3614 int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3615 SkScalar w, SkPoint pts[], int pow2) {
3616 const SkConic conic(p0, p1, p2, w);
3617 return conic.chopIntoQuadsPOW2(pts, pow2);
3618 }
3619
IsSimpleClosedRect(const SkPath & path,SkRect * rect,SkPath::Direction * direction,unsigned * start)3620 bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
3621 unsigned* start) {
3622 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
3623 return false;
3624 }
3625 SkPath::RawIter iter(path);
3626 SkPoint verbPts[4];
3627 SkPath::Verb v;
3628 SkPoint rectPts[5];
3629 int rectPtCnt = 0;
3630 while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) {
3631 switch (v) {
3632 case SkPath::kMove_Verb:
3633 if (0 != rectPtCnt) {
3634 return false;
3635 }
3636 rectPts[0] = verbPts[0];
3637 ++rectPtCnt;
3638 break;
3639 case SkPath::kLine_Verb:
3640 if (5 == rectPtCnt) {
3641 return false;
3642 }
3643 rectPts[rectPtCnt] = verbPts[1];
3644 ++rectPtCnt;
3645 break;
3646 case SkPath::kClose_Verb:
3647 if (4 == rectPtCnt) {
3648 rectPts[4] = rectPts[0];
3649 rectPtCnt = 5;
3650 }
3651 break;
3652 default:
3653 return false;
3654 }
3655 }
3656 if (rectPtCnt < 5) {
3657 return false;
3658 }
3659 if (rectPts[0] != rectPts[4]) {
3660 return false;
3661 }
3662 // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
3663 // and pts 1 and 2 the opposite vertical or horizontal edge).
3664 bool vec03IsVertical;
3665 if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
3666 rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
3667 // Make sure it has non-zero width and height
3668 if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
3669 return false;
3670 }
3671 vec03IsVertical = true;
3672 } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
3673 rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
3674 // Make sure it has non-zero width and height
3675 if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
3676 return false;
3677 }
3678 vec03IsVertical = false;
3679 } else {
3680 return false;
3681 }
3682 // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
3683 // set if it is on the bottom edge.
3684 unsigned sortFlags =
3685 ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
3686 ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
3687 switch (sortFlags) {
3688 case 0b00:
3689 rect->set(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
3690 *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
3691 *start = 0;
3692 break;
3693 case 0b01:
3694 rect->set(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
3695 *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
3696 *start = 1;
3697 break;
3698 case 0b10:
3699 rect->set(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
3700 *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
3701 *start = 3;
3702 break;
3703 case 0b11:
3704 rect->set(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
3705 *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
3706 *start = 2;
3707 break;
3708 }
3709 return true;
3710 }
3711
DrawArcIsConvex(SkScalar sweepAngle,bool useCenter,bool isFillNoPathEffect)3712 bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
3713 if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3714 // This gets converted to an oval.
3715 return true;
3716 }
3717 if (useCenter) {
3718 // This is a pie wedge. It's convex if the angle is <= 180.
3719 return SkScalarAbs(sweepAngle) <= 180.f;
3720 }
3721 // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
3722 // to a secant, i.e. convex.
3723 return SkScalarAbs(sweepAngle) <= 360.f;
3724 }
3725
CreateDrawArcPath(SkPath * path,const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,bool isFillNoPathEffect)3726 void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
3727 SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
3728 SkASSERT(!oval.isEmpty());
3729 SkASSERT(sweepAngle);
3730
3731 path->reset();
3732 path->setIsVolatile(true);
3733 path->setFillType(SkPath::kWinding_FillType);
3734 if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
3735 path->addOval(oval);
3736 SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
3737 return;
3738 }
3739 if (useCenter) {
3740 path->moveTo(oval.centerX(), oval.centerY());
3741 }
3742 auto firstDir =
3743 sweepAngle > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
3744 bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect);
3745 // Arc to mods at 360 and drawArc is not supposed to.
3746 bool forceMoveTo = !useCenter;
3747 while (sweepAngle <= -360.f) {
3748 path->arcTo(oval, startAngle, -180.f, forceMoveTo);
3749 startAngle -= 180.f;
3750 path->arcTo(oval, startAngle, -180.f, false);
3751 startAngle -= 180.f;
3752 forceMoveTo = false;
3753 sweepAngle += 360.f;
3754 }
3755 while (sweepAngle >= 360.f) {
3756 path->arcTo(oval, startAngle, 180.f, forceMoveTo);
3757 startAngle += 180.f;
3758 path->arcTo(oval, startAngle, 180.f, false);
3759 startAngle += 180.f;
3760 forceMoveTo = false;
3761 sweepAngle -= 360.f;
3762 }
3763 path->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
3764 if (useCenter) {
3765 path->close();
3766 }
3767 path->setConvexity(convex ? SkPath::kConvex_Convexity : SkPath::kConcave_Convexity);
3768 path->setFirstDirection(firstDir);
3769 }
3770
3771 ///////////////////////////////////////////////////////////////////////////////////////////////////
3772 #include "include/private/SkNx.h"
3773
compute_quad_extremas(const SkPoint src[3],SkPoint extremas[3])3774 static int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) {
3775 SkScalar ts[2];
3776 int n = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts);
3777 n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]);
3778 SkASSERT(n >= 0 && n <= 2);
3779 for (int i = 0; i < n; ++i) {
3780 extremas[i] = SkEvalQuadAt(src, ts[i]);
3781 }
3782 extremas[n] = src[2];
3783 return n + 1;
3784 }
3785
compute_conic_extremas(const SkPoint src[3],SkScalar w,SkPoint extremas[3])3786 static int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) {
3787 SkConic conic(src[0], src[1], src[2], w);
3788 SkScalar ts[2];
3789 int n = conic.findXExtrema(ts);
3790 n += conic.findYExtrema(&ts[n]);
3791 SkASSERT(n >= 0 && n <= 2);
3792 for (int i = 0; i < n; ++i) {
3793 extremas[i] = conic.evalAt(ts[i]);
3794 }
3795 extremas[n] = src[2];
3796 return n + 1;
3797 }
3798
compute_cubic_extremas(const SkPoint src[4],SkPoint extremas[5])3799 static int compute_cubic_extremas(const SkPoint src[4], SkPoint extremas[5]) {
3800 SkScalar ts[4];
3801 int n = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts);
3802 n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]);
3803 SkASSERT(n >= 0 && n <= 4);
3804 for (int i = 0; i < n; ++i) {
3805 SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr);
3806 }
3807 extremas[n] = src[3];
3808 return n + 1;
3809 }
3810
computeTightBounds() const3811 SkRect SkPath::computeTightBounds() const {
3812 if (0 == this->countVerbs()) {
3813 return SkRect::MakeEmpty();
3814 }
3815
3816 if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
3817 return this->getBounds();
3818 }
3819
3820 SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1
3821 SkPoint pts[4];
3822 SkPath::RawIter iter(*this);
3823
3824 // initial with the first MoveTo, so we don't have to check inside the switch
3825 Sk2s min, max;
3826 min = max = from_point(this->getPoint(0));
3827 for (;;) {
3828 int count = 0;
3829 switch (iter.next(pts)) {
3830 case SkPath::kMove_Verb:
3831 extremas[0] = pts[0];
3832 count = 1;
3833 break;
3834 case SkPath::kLine_Verb:
3835 extremas[0] = pts[1];
3836 count = 1;
3837 break;
3838 case SkPath::kQuad_Verb:
3839 count = compute_quad_extremas(pts, extremas);
3840 break;
3841 case SkPath::kConic_Verb:
3842 count = compute_conic_extremas(pts, iter.conicWeight(), extremas);
3843 break;
3844 case SkPath::kCubic_Verb:
3845 count = compute_cubic_extremas(pts, extremas);
3846 break;
3847 case SkPath::kClose_Verb:
3848 break;
3849 case SkPath::kDone_Verb:
3850 goto DONE;
3851 }
3852 for (int i = 0; i < count; ++i) {
3853 Sk2s tmp = from_point(extremas[i]);
3854 min = Sk2s::Min(min, tmp);
3855 max = Sk2s::Max(max, tmp);
3856 }
3857 }
3858 DONE:
3859 SkRect bounds;
3860 min.store((SkPoint*)&bounds.fLeft);
3861 max.store((SkPoint*)&bounds.fRight);
3862 return bounds;
3863 }
3864
IsLineDegenerate(const SkPoint & p1,const SkPoint & p2,bool exact)3865 bool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
3866 return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2);
3867 }
3868
IsQuadDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,bool exact)3869 bool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
3870 const SkPoint& p3, bool exact) {
3871 return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3872 SkPointPriv::EqualsWithinTolerance(p2, p3);
3873 }
3874
IsCubicDegenerate(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,const SkPoint & p4,bool exact)3875 bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
3876 const SkPoint& p3, const SkPoint& p4, bool exact) {
3877 return exact ? p1 == p2 && p2 == p3 && p3 == p4 :
3878 SkPointPriv::EqualsWithinTolerance(p1, p2) &&
3879 SkPointPriv::EqualsWithinTolerance(p2, p3) &&
3880 SkPointPriv::EqualsWithinTolerance(p3, p4);
3881 }
3882