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 "SkBuffer.h"
9 #include "SkCubicClipper.h"
10 #include "SkErrorInternals.h"
11 #include "SkGeometry.h"
12 #include "SkMath.h"
13 #include "SkPathPriv.h"
14 #include "SkPathRef.h"
15 #include "SkRRect.h"
16
17 ////////////////////////////////////////////////////////////////////////////
18
19 /**
20 * Path.bounds is defined to be the bounds of all the control points.
21 * If we called bounds.join(r) we would skip r if r was empty, which breaks
22 * our promise. Hence we have a custom joiner that doesn't look at emptiness
23 */
joinNoEmptyChecks(SkRect * dst,const SkRect & src)24 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
25 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
26 dst->fTop = SkMinScalar(dst->fTop, src.fTop);
27 dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
28 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
29 }
30
is_degenerate(const SkPath & path)31 static bool is_degenerate(const SkPath& path) {
32 SkPath::Iter iter(path, false);
33 SkPoint pts[4];
34 return SkPath::kDone_Verb == iter.next(pts);
35 }
36
37 class SkAutoDisableDirectionCheck {
38 public:
SkAutoDisableDirectionCheck(SkPath * path)39 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
40 fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->fFirstDirection.load());
41 }
42
~SkAutoDisableDirectionCheck()43 ~SkAutoDisableDirectionCheck() {
44 fPath->fFirstDirection = fSaved;
45 }
46
47 private:
48 SkPath* fPath;
49 SkPathPriv::FirstDirection fSaved;
50 };
51 #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
52
53 /* This guy's constructor/destructor bracket a path editing operation. It is
54 used when we know the bounds of the amount we are going to add to the path
55 (usually a new contour, but not required).
56
57 It captures some state about the path up front (i.e. if it already has a
58 cached bounds), and then if it can, it updates the cache bounds explicitly,
59 avoiding the need to revisit all of the points in getBounds().
60
61 It also notes if the path was originally degenerate, and if so, sets
62 isConvex to true. Thus it can only be used if the contour being added is
63 convex.
64 */
65 class SkAutoPathBoundsUpdate {
66 public:
SkAutoPathBoundsUpdate(SkPath * path,const SkRect & r)67 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
68 this->init(path);
69 }
70
SkAutoPathBoundsUpdate(SkPath * path,SkScalar left,SkScalar top,SkScalar right,SkScalar bottom)71 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
72 SkScalar right, SkScalar bottom) {
73 fRect.set(left, top, right, bottom);
74 this->init(path);
75 }
76
~SkAutoPathBoundsUpdate()77 ~SkAutoPathBoundsUpdate() {
78 fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
79 : SkPath::kUnknown_Convexity);
80 if (fEmpty || fHasValidBounds) {
81 fPath->setBounds(fRect);
82 }
83 }
84
85 private:
86 SkPath* fPath;
87 SkRect fRect;
88 bool fHasValidBounds;
89 bool fDegenerate;
90 bool fEmpty;
91
init(SkPath * path)92 void init(SkPath* path) {
93 // Cannot use fRect for our bounds unless we know it is sorted
94 fRect.sort();
95 fPath = path;
96 // Mark the path's bounds as dirty if (1) they are, or (2) the path
97 // is non-finite, and therefore its bounds are not meaningful
98 fHasValidBounds = path->hasComputedBounds() && path->isFinite();
99 fEmpty = path->isEmpty();
100 if (fHasValidBounds && !fEmpty) {
101 joinNoEmptyChecks(&fRect, fPath->getBounds());
102 }
103 fDegenerate = is_degenerate(*path);
104 }
105 };
106 #define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
107
108 ////////////////////////////////////////////////////////////////////////////
109
110 /*
111 Stores the verbs and points as they are given to us, with exceptions:
112 - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
113 - we insert a Move(0,0) if Line | Quad | Cubic is our first command
114
115 The iterator does more cleanup, especially if forceClose == true
116 1. If we encounter degenerate segments, remove them
117 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
118 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
119 4. if we encounter Line | Quad | Cubic after Close, cons up a Move
120 */
121
122 ////////////////////////////////////////////////////////////////////////////
123
124 // flag to require a moveTo if we begin with something else, like lineTo etc.
125 #define INITIAL_LASTMOVETOINDEX_VALUE ~0
126
SkPath()127 SkPath::SkPath()
128 : fPathRef(SkPathRef::CreateEmpty()) {
129 this->resetFields();
130 fIsVolatile = false;
131 }
132
resetFields()133 void SkPath::resetFields() {
134 //fPathRef is assumed to have been emptied by the caller.
135 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
136 fFillType = kWinding_FillType;
137 fConvexity = kUnknown_Convexity;
138 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
139
140 // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we
141 // don't want to muck with it if it's been set to something non-nullptr.
142 }
143
SkPath(const SkPath & that)144 SkPath::SkPath(const SkPath& that)
145 : fPathRef(SkRef(that.fPathRef.get())) {
146 this->copyFields(that);
147 SkDEBUGCODE(that.validate();)
148 }
149
~SkPath()150 SkPath::~SkPath() {
151 SkDEBUGCODE(this->validate();)
152 }
153
operator =(const SkPath & that)154 SkPath& SkPath::operator=(const SkPath& that) {
155 SkDEBUGCODE(that.validate();)
156
157 if (this != &that) {
158 fPathRef.reset(SkRef(that.fPathRef.get()));
159 this->copyFields(that);
160 }
161 SkDEBUGCODE(this->validate();)
162 return *this;
163 }
164
copyFields(const SkPath & that)165 void SkPath::copyFields(const SkPath& that) {
166 //fPathRef is assumed to have been set by the caller.
167 fLastMoveToIndex = that.fLastMoveToIndex;
168 fFillType = that.fFillType;
169 fConvexity = that.fConvexity;
170 // Simulate fFirstDirection = that.fFirstDirection;
171 fFirstDirection.store(that.fFirstDirection.load());
172 fIsVolatile = that.fIsVolatile;
173 }
174
operator ==(const SkPath & a,const SkPath & b)175 bool operator==(const SkPath& a, const SkPath& b) {
176 // note: don't need to look at isConvex or bounds, since just comparing the
177 // raw data is sufficient.
178 return &a == &b ||
179 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
180 }
181
swap(SkPath & that)182 void SkPath::swap(SkPath& that) {
183 if (this != &that) {
184 fPathRef.swap(that.fPathRef);
185 SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
186 SkTSwap<uint8_t>(fFillType, that.fFillType);
187 SkTSwap<uint8_t>(fConvexity, that.fConvexity);
188 // Simulate SkTSwap<uint8_t>(fFirstDirection, that.fFirstDirection);
189 uint8_t temp = fFirstDirection;
190 fFirstDirection.store(that.fFirstDirection.load());
191 that.fFirstDirection.store(temp);
192 SkTSwap<SkBool8>(fIsVolatile, that.fIsVolatile);
193 }
194 }
195
isInterpolatable(const SkPath & compare) const196 bool SkPath::isInterpolatable(const SkPath& compare) const {
197 int count = fPathRef->countVerbs();
198 if (count != compare.fPathRef->countVerbs()) {
199 return false;
200 }
201 if (!count) {
202 return true;
203 }
204 if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
205 count)) {
206 return false;
207 }
208 return !fPathRef->countWeights() ||
209 !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
210 fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
211 }
212
interpolate(const SkPath & ending,SkScalar weight,SkPath * out) const213 bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
214 int verbCount = fPathRef->countVerbs();
215 if (verbCount != ending.fPathRef->countVerbs()) {
216 return false;
217 }
218 if (!verbCount) {
219 return true;
220 }
221 out->reset();
222 out->addPath(*this);
223 fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef);
224 return true;
225 }
226
check_edge_against_rect(const SkPoint & p0,const SkPoint & p1,const SkRect & rect,SkPathPriv::FirstDirection dir)227 static inline bool check_edge_against_rect(const SkPoint& p0,
228 const SkPoint& p1,
229 const SkRect& rect,
230 SkPathPriv::FirstDirection dir) {
231 const SkPoint* edgeBegin;
232 SkVector v;
233 if (SkPathPriv::kCW_FirstDirection == dir) {
234 v = p1 - p0;
235 edgeBegin = &p0;
236 } else {
237 v = p0 - p1;
238 edgeBegin = &p1;
239 }
240 if (v.fX || v.fY) {
241 // check the cross product of v with the vec from edgeBegin to each rect corner
242 SkScalar yL = SkScalarMul(v.fY, rect.fLeft - edgeBegin->fX);
243 SkScalar xT = SkScalarMul(v.fX, rect.fTop - edgeBegin->fY);
244 SkScalar yR = SkScalarMul(v.fY, rect.fRight - edgeBegin->fX);
245 SkScalar xB = SkScalarMul(v.fX, rect.fBottom - edgeBegin->fY);
246 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
247 return false;
248 }
249 }
250 return true;
251 }
252
conservativelyContainsRect(const SkRect & rect) const253 bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
254 // This only handles non-degenerate convex paths currently.
255 if (kConvex_Convexity != this->getConvexity()) {
256 return false;
257 }
258
259 SkPathPriv::FirstDirection direction;
260 if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
261 return false;
262 }
263
264 SkPoint firstPt;
265 SkPoint prevPt;
266 RawIter iter(*this);
267 SkPath::Verb verb;
268 SkPoint pts[4];
269 SkDEBUGCODE(int moveCnt = 0;)
270 SkDEBUGCODE(int segmentCount = 0;)
271 SkDEBUGCODE(int closeCount = 0;)
272
273 while ((verb = iter.next(pts)) != kDone_Verb) {
274 int nextPt = -1;
275 switch (verb) {
276 case kMove_Verb:
277 SkASSERT(!segmentCount && !closeCount);
278 SkDEBUGCODE(++moveCnt);
279 firstPt = prevPt = pts[0];
280 break;
281 case kLine_Verb:
282 nextPt = 1;
283 SkASSERT(moveCnt && !closeCount);
284 SkDEBUGCODE(++segmentCount);
285 break;
286 case kQuad_Verb:
287 case kConic_Verb:
288 SkASSERT(moveCnt && !closeCount);
289 SkDEBUGCODE(++segmentCount);
290 nextPt = 2;
291 break;
292 case kCubic_Verb:
293 SkASSERT(moveCnt && !closeCount);
294 SkDEBUGCODE(++segmentCount);
295 nextPt = 3;
296 break;
297 case kClose_Verb:
298 SkDEBUGCODE(++closeCount;)
299 break;
300 default:
301 SkDEBUGFAIL("unknown verb");
302 }
303 if (-1 != nextPt) {
304 if (SkPath::kConic_Verb == verb) {
305 SkConic orig;
306 orig.set(pts, iter.conicWeight());
307 SkPoint quadPts[5];
308 int count = orig.chopIntoQuadsPOW2(quadPts, 1);
309 SkASSERT_RELEASE(2 == count);
310
311 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
312 return false;
313 }
314 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
315 return false;
316 }
317 } else {
318 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
319 return false;
320 }
321 }
322 prevPt = pts[nextPt];
323 }
324 }
325
326 return check_edge_against_rect(prevPt, firstPt, rect, direction);
327 }
328
getGenerationID() const329 uint32_t SkPath::getGenerationID() const {
330 uint32_t genID = fPathRef->genID();
331 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
332 SkASSERT((unsigned)fFillType < (1 << (32 - kPathRefGenIDBitCnt)));
333 genID |= static_cast<uint32_t>(fFillType) << kPathRefGenIDBitCnt;
334 #endif
335 return genID;
336 }
337
reset()338 void SkPath::reset() {
339 SkDEBUGCODE(this->validate();)
340
341 fPathRef.reset(SkPathRef::CreateEmpty());
342 this->resetFields();
343 }
344
rewind()345 void SkPath::rewind() {
346 SkDEBUGCODE(this->validate();)
347
348 SkPathRef::Rewind(&fPathRef);
349 this->resetFields();
350 }
351
isLastContourClosed() const352 bool SkPath::isLastContourClosed() const {
353 int verbCount = fPathRef->countVerbs();
354 if (0 == verbCount) {
355 return false;
356 }
357 return kClose_Verb == fPathRef->atVerb(verbCount - 1);
358 }
359
isLine(SkPoint line[2]) const360 bool SkPath::isLine(SkPoint line[2]) const {
361 int verbCount = fPathRef->countVerbs();
362
363 if (2 == verbCount) {
364 SkASSERT(kMove_Verb == fPathRef->atVerb(0));
365 if (kLine_Verb == fPathRef->atVerb(1)) {
366 SkASSERT(2 == fPathRef->countPoints());
367 if (line) {
368 const SkPoint* pts = fPathRef->points();
369 line[0] = pts[0];
370 line[1] = pts[1];
371 }
372 return true;
373 }
374 }
375 return false;
376 }
377
378 /*
379 Determines if path is a rect by keeping track of changes in direction
380 and looking for a loop either clockwise or counterclockwise.
381
382 The direction is computed such that:
383 0: vertical up
384 1: horizontal left
385 2: vertical down
386 3: horizontal right
387
388 A rectangle cycles up/right/down/left or up/left/down/right.
389
390 The test fails if:
391 The path is closed, and followed by a line.
392 A second move creates a new endpoint.
393 A diagonal line is parsed.
394 There's more than four changes of direction.
395 There's a discontinuity on the line (e.g., a move in the middle)
396 The line reverses direction.
397 The path contains a quadratic or cubic.
398 The path contains fewer than four points.
399 *The rectangle doesn't complete a cycle.
400 *The final point isn't equal to the first point.
401
402 *These last two conditions we relax if we have a 3-edge path that would
403 form a rectangle if it were closed (as we do when we fill a path)
404
405 It's OK if the path has:
406 Several colinear line segments composing a rectangle side.
407 Single points on the rectangle side.
408
409 The direction takes advantage of the corners found since opposite sides
410 must travel in opposite directions.
411
412 FIXME: Allow colinear quads and cubics to be treated like lines.
413 FIXME: If the API passes fill-only, return true if the filled stroke
414 is a rectangle, though the caller failed to close the path.
415
416 first,last,next direction state-machine:
417 0x1 is set if the segment is horizontal
418 0x2 is set if the segment is moving to the right or down
419 thus:
420 two directions are opposites iff (dirA ^ dirB) == 0x2
421 two directions are perpendicular iff (dirA ^ dirB) == 0x1
422
423 */
rect_make_dir(SkScalar dx,SkScalar dy)424 static int rect_make_dir(SkScalar dx, SkScalar dy) {
425 return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
426 }
isRectContour(bool allowPartial,int * currVerb,const SkPoint ** ptsPtr,bool * isClosed,Direction * direction) const427 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
428 bool* isClosed, Direction* direction) const {
429 int corners = 0;
430 SkPoint first, last;
431 const SkPoint* pts = *ptsPtr;
432 const SkPoint* savePts = nullptr;
433 first.set(0, 0);
434 last.set(0, 0);
435 int firstDirection = 0;
436 int lastDirection = 0;
437 int nextDirection = 0;
438 bool closedOrMoved = false;
439 bool autoClose = false;
440 bool insertClose = false;
441 int verbCnt = fPathRef->countVerbs();
442 while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
443 uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
444 switch (verb) {
445 case kClose_Verb:
446 savePts = pts;
447 pts = *ptsPtr;
448 autoClose = true;
449 insertClose = false;
450 case kLine_Verb: {
451 SkScalar left = last.fX;
452 SkScalar top = last.fY;
453 SkScalar right = pts->fX;
454 SkScalar bottom = pts->fY;
455 ++pts;
456 if (left != right && top != bottom) {
457 return false; // diagonal
458 }
459 if (left == right && top == bottom) {
460 break; // single point on side OK
461 }
462 nextDirection = rect_make_dir(right - left, bottom - top);
463 if (0 == corners) {
464 firstDirection = nextDirection;
465 first = last;
466 last = pts[-1];
467 corners = 1;
468 closedOrMoved = false;
469 break;
470 }
471 if (closedOrMoved) {
472 return false; // closed followed by a line
473 }
474 if (autoClose && nextDirection == firstDirection) {
475 break; // colinear with first
476 }
477 closedOrMoved = autoClose;
478 if (lastDirection != nextDirection) {
479 if (++corners > 4) {
480 return false; // too many direction changes
481 }
482 }
483 last = pts[-1];
484 if (lastDirection == nextDirection) {
485 break; // colinear segment
486 }
487 // Possible values for corners are 2, 3, and 4.
488 // When corners == 3, nextDirection opposes firstDirection.
489 // Otherwise, nextDirection at corner 2 opposes corner 4.
490 int turn = firstDirection ^ (corners - 1);
491 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
492 if ((directionCycle ^ turn) != nextDirection) {
493 return false; // direction didn't follow cycle
494 }
495 break;
496 }
497 case kQuad_Verb:
498 case kConic_Verb:
499 case kCubic_Verb:
500 return false; // quadratic, cubic not allowed
501 case kMove_Verb:
502 if (allowPartial && !autoClose && firstDirection) {
503 insertClose = true;
504 *currVerb -= 1; // try move again afterwards
505 goto addMissingClose;
506 }
507 last = *pts++;
508 closedOrMoved = true;
509 break;
510 default:
511 SkDEBUGFAIL("unexpected verb");
512 break;
513 }
514 *currVerb += 1;
515 lastDirection = nextDirection;
516 addMissingClose:
517 ;
518 }
519 // Success if 4 corners and first point equals last
520 bool result = 4 == corners && (first == last || autoClose);
521 if (!result) {
522 // check if we are just an incomplete rectangle, in which case we can
523 // return true, but not claim to be closed.
524 // e.g.
525 // 3 sided rectangle
526 // 4 sided but the last edge is not long enough to reach the start
527 //
528 SkScalar closeX = first.x() - last.x();
529 SkScalar closeY = first.y() - last.y();
530 if (closeX && closeY) {
531 return false; // we're diagonal, abort (can we ever reach this?)
532 }
533 int closeDirection = rect_make_dir(closeX, closeY);
534 // make sure the close-segment doesn't double-back on itself
535 if (3 == corners || (4 == corners && closeDirection == lastDirection)) {
536 result = true;
537 autoClose = false; // we are not closed
538 }
539 }
540 if (savePts) {
541 *ptsPtr = savePts;
542 }
543 if (result && isClosed) {
544 *isClosed = autoClose;
545 }
546 if (result && direction) {
547 *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
548 }
549 return result;
550 }
551
isRect(SkRect * rect,bool * isClosed,Direction * direction) const552 bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
553 SkDEBUGCODE(this->validate();)
554 int currVerb = 0;
555 const SkPoint* pts = fPathRef->points();
556 const SkPoint* first = pts;
557 if (!this->isRectContour(false, &currVerb, &pts, isClosed, direction)) {
558 return false;
559 }
560 if (rect) {
561 int32_t num = SkToS32(pts - first);
562 if (num) {
563 rect->set(first, num);
564 } else {
565 // 'pts' isn't updated for open rects
566 *rect = this->getBounds();
567 }
568 }
569 return true;
570 }
571
isNestedFillRects(SkRect rects[2],Direction dirs[2]) const572 bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
573 SkDEBUGCODE(this->validate();)
574 int currVerb = 0;
575 const SkPoint* pts = fPathRef->points();
576 const SkPoint* first = pts;
577 Direction testDirs[2];
578 if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0])) {
579 return false;
580 }
581 const SkPoint* last = pts;
582 SkRect testRects[2];
583 bool isClosed;
584 if (isRectContour(false, &currVerb, &pts, &isClosed, &testDirs[1])) {
585 testRects[0].set(first, SkToS32(last - first));
586 if (!isClosed) {
587 pts = fPathRef->points() + fPathRef->countPoints();
588 }
589 testRects[1].set(last, SkToS32(pts - last));
590 if (testRects[0].contains(testRects[1])) {
591 if (rects) {
592 rects[0] = testRects[0];
593 rects[1] = testRects[1];
594 }
595 if (dirs) {
596 dirs[0] = testDirs[0];
597 dirs[1] = testDirs[1];
598 }
599 return true;
600 }
601 if (testRects[1].contains(testRects[0])) {
602 if (rects) {
603 rects[0] = testRects[1];
604 rects[1] = testRects[0];
605 }
606 if (dirs) {
607 dirs[0] = testDirs[1];
608 dirs[1] = testDirs[0];
609 }
610 return true;
611 }
612 }
613 return false;
614 }
615
countPoints() const616 int SkPath::countPoints() const {
617 return fPathRef->countPoints();
618 }
619
getPoints(SkPoint dst[],int max) const620 int SkPath::getPoints(SkPoint dst[], int max) const {
621 SkDEBUGCODE(this->validate();)
622
623 SkASSERT(max >= 0);
624 SkASSERT(!max || dst);
625 int count = SkMin32(max, fPathRef->countPoints());
626 sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
627 return fPathRef->countPoints();
628 }
629
getPoint(int index) const630 SkPoint SkPath::getPoint(int index) const {
631 if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
632 return fPathRef->atPoint(index);
633 }
634 return SkPoint::Make(0, 0);
635 }
636
countVerbs() const637 int SkPath::countVerbs() const {
638 return fPathRef->countVerbs();
639 }
640
copy_verbs_reverse(uint8_t * inorderDst,const uint8_t * reversedSrc,int count)641 static inline void copy_verbs_reverse(uint8_t* inorderDst,
642 const uint8_t* reversedSrc,
643 int count) {
644 for (int i = 0; i < count; ++i) {
645 inorderDst[i] = reversedSrc[~i];
646 }
647 }
648
getVerbs(uint8_t dst[],int max) const649 int SkPath::getVerbs(uint8_t dst[], int max) const {
650 SkDEBUGCODE(this->validate();)
651
652 SkASSERT(max >= 0);
653 SkASSERT(!max || dst);
654 int count = SkMin32(max, fPathRef->countVerbs());
655 copy_verbs_reverse(dst, fPathRef->verbs(), count);
656 return fPathRef->countVerbs();
657 }
658
getLastPt(SkPoint * lastPt) const659 bool SkPath::getLastPt(SkPoint* lastPt) const {
660 SkDEBUGCODE(this->validate();)
661
662 int count = fPathRef->countPoints();
663 if (count > 0) {
664 if (lastPt) {
665 *lastPt = fPathRef->atPoint(count - 1);
666 }
667 return true;
668 }
669 if (lastPt) {
670 lastPt->set(0, 0);
671 }
672 return false;
673 }
674
setPt(int index,SkScalar x,SkScalar y)675 void SkPath::setPt(int index, SkScalar x, SkScalar y) {
676 SkDEBUGCODE(this->validate();)
677
678 int count = fPathRef->countPoints();
679 if (count <= index) {
680 return;
681 } else {
682 SkPathRef::Editor ed(&fPathRef);
683 ed.atPoint(index)->set(x, y);
684 }
685 }
686
setLastPt(SkScalar x,SkScalar y)687 void SkPath::setLastPt(SkScalar x, SkScalar y) {
688 SkDEBUGCODE(this->validate();)
689
690 int count = fPathRef->countPoints();
691 if (count == 0) {
692 this->moveTo(x, y);
693 } else {
694 SkPathRef::Editor ed(&fPathRef);
695 ed.atPoint(count-1)->set(x, y);
696 }
697 }
698
setConvexity(Convexity c)699 void SkPath::setConvexity(Convexity c) {
700 if (fConvexity != c) {
701 fConvexity = c;
702 }
703 }
704
705 //////////////////////////////////////////////////////////////////////////////
706 // Construction methods
707
708 #define DIRTY_AFTER_EDIT \
709 do { \
710 fConvexity = kUnknown_Convexity; \
711 fFirstDirection = SkPathPriv::kUnknown_FirstDirection; \
712 } while (0)
713
incReserve(U16CPU inc)714 void SkPath::incReserve(U16CPU inc) {
715 SkDEBUGCODE(this->validate();)
716 SkPathRef::Editor(&fPathRef, inc, inc);
717 SkDEBUGCODE(this->validate();)
718 }
719
moveTo(SkScalar x,SkScalar y)720 void SkPath::moveTo(SkScalar x, SkScalar y) {
721 SkDEBUGCODE(this->validate();)
722
723 SkPathRef::Editor ed(&fPathRef);
724
725 // remember our index
726 fLastMoveToIndex = fPathRef->countPoints();
727
728 ed.growForVerb(kMove_Verb)->set(x, y);
729
730 DIRTY_AFTER_EDIT;
731 }
732
rMoveTo(SkScalar x,SkScalar y)733 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
734 SkPoint pt;
735 this->getLastPt(&pt);
736 this->moveTo(pt.fX + x, pt.fY + y);
737 }
738
injectMoveToIfNeeded()739 void SkPath::injectMoveToIfNeeded() {
740 if (fLastMoveToIndex < 0) {
741 SkScalar x, y;
742 if (fPathRef->countVerbs() == 0) {
743 x = y = 0;
744 } else {
745 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
746 x = pt.fX;
747 y = pt.fY;
748 }
749 this->moveTo(x, y);
750 }
751 }
752
lineTo(SkScalar x,SkScalar y)753 void SkPath::lineTo(SkScalar x, SkScalar y) {
754 SkDEBUGCODE(this->validate();)
755
756 this->injectMoveToIfNeeded();
757
758 SkPathRef::Editor ed(&fPathRef);
759 ed.growForVerb(kLine_Verb)->set(x, y);
760
761 DIRTY_AFTER_EDIT;
762 }
763
rLineTo(SkScalar x,SkScalar y)764 void SkPath::rLineTo(SkScalar x, SkScalar y) {
765 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
766 SkPoint pt;
767 this->getLastPt(&pt);
768 this->lineTo(pt.fX + x, pt.fY + y);
769 }
770
quadTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)771 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
772 SkDEBUGCODE(this->validate();)
773
774 this->injectMoveToIfNeeded();
775
776 SkPathRef::Editor ed(&fPathRef);
777 SkPoint* pts = ed.growForVerb(kQuad_Verb);
778 pts[0].set(x1, y1);
779 pts[1].set(x2, y2);
780
781 DIRTY_AFTER_EDIT;
782 }
783
rQuadTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)784 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
785 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
786 SkPoint pt;
787 this->getLastPt(&pt);
788 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
789 }
790
conicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar w)791 void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
792 SkScalar w) {
793 // check for <= 0 or NaN with this test
794 if (!(w > 0)) {
795 this->lineTo(x2, y2);
796 } else if (!SkScalarIsFinite(w)) {
797 this->lineTo(x1, y1);
798 this->lineTo(x2, y2);
799 } else if (SK_Scalar1 == w) {
800 this->quadTo(x1, y1, x2, y2);
801 } else {
802 SkDEBUGCODE(this->validate();)
803
804 this->injectMoveToIfNeeded();
805
806 SkPathRef::Editor ed(&fPathRef);
807 SkPoint* pts = ed.growForVerb(kConic_Verb, w);
808 pts[0].set(x1, y1);
809 pts[1].set(x2, y2);
810
811 DIRTY_AFTER_EDIT;
812 }
813 }
814
rConicTo(SkScalar dx1,SkScalar dy1,SkScalar dx2,SkScalar dy2,SkScalar w)815 void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
816 SkScalar w) {
817 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
818 SkPoint pt;
819 this->getLastPt(&pt);
820 this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
821 }
822
cubicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)823 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
824 SkScalar x3, SkScalar y3) {
825 SkDEBUGCODE(this->validate();)
826
827 this->injectMoveToIfNeeded();
828
829 SkPathRef::Editor ed(&fPathRef);
830 SkPoint* pts = ed.growForVerb(kCubic_Verb);
831 pts[0].set(x1, y1);
832 pts[1].set(x2, y2);
833 pts[2].set(x3, y3);
834
835 DIRTY_AFTER_EDIT;
836 }
837
rCubicTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)838 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
839 SkScalar x3, SkScalar y3) {
840 this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
841 SkPoint pt;
842 this->getLastPt(&pt);
843 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
844 pt.fX + x3, pt.fY + y3);
845 }
846
close()847 void SkPath::close() {
848 SkDEBUGCODE(this->validate();)
849
850 int count = fPathRef->countVerbs();
851 if (count > 0) {
852 switch (fPathRef->atVerb(count - 1)) {
853 case kLine_Verb:
854 case kQuad_Verb:
855 case kConic_Verb:
856 case kCubic_Verb:
857 case kMove_Verb: {
858 SkPathRef::Editor ed(&fPathRef);
859 ed.growForVerb(kClose_Verb);
860 break;
861 }
862 case kClose_Verb:
863 // don't add a close if it's the first verb or a repeat
864 break;
865 default:
866 SkDEBUGFAIL("unexpected verb");
867 break;
868 }
869 }
870
871 // signal that we need a moveTo to follow us (unless we're done)
872 #if 0
873 if (fLastMoveToIndex >= 0) {
874 fLastMoveToIndex = ~fLastMoveToIndex;
875 }
876 #else
877 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
878 #endif
879 }
880
881 ///////////////////////////////////////////////////////////////////////////////
882
883 namespace {
884
885 template <unsigned N>
886 class PointIterator {
887 public:
PointIterator(SkPath::Direction dir,unsigned startIndex)888 PointIterator(SkPath::Direction dir, unsigned startIndex)
889 : fCurrent(startIndex % N)
890 , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
891
current() const892 const SkPoint& current() const {
893 SkASSERT(fCurrent < N);
894 return fPts[fCurrent];
895 }
896
next()897 const SkPoint& next() {
898 fCurrent = (fCurrent + fAdvance) % N;
899 return this->current();
900 }
901
902 protected:
903 SkPoint fPts[N];
904
905 private:
906 unsigned fCurrent;
907 unsigned fAdvance;
908 };
909
910 class RectPointIterator : public PointIterator<4> {
911 public:
RectPointIterator(const SkRect & rect,SkPath::Direction dir,unsigned startIndex)912 RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
913 : PointIterator(dir, startIndex) {
914
915 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
916 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
917 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
918 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
919 }
920 };
921
922 class OvalPointIterator : public PointIterator<4> {
923 public:
OvalPointIterator(const SkRect & oval,SkPath::Direction dir,unsigned startIndex)924 OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
925 : PointIterator(dir, startIndex) {
926
927 const SkScalar cx = oval.centerX();
928 const SkScalar cy = oval.centerY();
929
930 fPts[0] = SkPoint::Make(cx, oval.fTop);
931 fPts[1] = SkPoint::Make(oval.fRight, cy);
932 fPts[2] = SkPoint::Make(cx, oval.fBottom);
933 fPts[3] = SkPoint::Make(oval.fLeft, cy);
934 }
935 };
936
937 class RRectPointIterator : public PointIterator<8> {
938 public:
RRectPointIterator(const SkRRect & rrect,SkPath::Direction dir,unsigned startIndex)939 RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
940 : PointIterator(dir, startIndex) {
941
942 const SkRect& bounds = rrect.getBounds();
943 const SkScalar L = bounds.fLeft;
944 const SkScalar T = bounds.fTop;
945 const SkScalar R = bounds.fRight;
946 const SkScalar B = bounds.fBottom;
947
948 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
949 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
950 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
951 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
952 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
953 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
954 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
955 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
956 }
957 };
958
959 } // anonymous namespace
960
assert_known_direction(int dir)961 static void assert_known_direction(int dir) {
962 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
963 }
964
addRect(const SkRect & rect,Direction dir)965 void SkPath::addRect(const SkRect& rect, Direction dir) {
966 this->addRect(rect, dir, 0);
967 }
968
addRect(SkScalar left,SkScalar top,SkScalar right,SkScalar bottom,Direction dir)969 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
970 SkScalar bottom, Direction dir) {
971 this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
972 }
973
addRect(const SkRect & rect,Direction dir,unsigned startIndex)974 void SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
975 assert_known_direction(dir);
976 fFirstDirection = this->hasOnlyMoveTos() ?
977 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
978 SkAutoDisableDirectionCheck addc(this);
979 SkAutoPathBoundsUpdate apbu(this, rect);
980
981 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
982
983 const int kVerbs = 5; // moveTo + 3x lineTo + close
984 this->incReserve(kVerbs);
985
986 RectPointIterator iter(rect, dir, startIndex);
987
988 this->moveTo(iter.current());
989 this->lineTo(iter.next());
990 this->lineTo(iter.next());
991 this->lineTo(iter.next());
992 this->close();
993
994 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
995 }
996
addPoly(const SkPoint pts[],int count,bool close)997 void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
998 SkDEBUGCODE(this->validate();)
999 if (count <= 0) {
1000 return;
1001 }
1002
1003 fLastMoveToIndex = fPathRef->countPoints();
1004
1005 // +close makes room for the extra kClose_Verb
1006 SkPathRef::Editor ed(&fPathRef, count+close, count);
1007
1008 ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
1009 if (count > 1) {
1010 SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
1011 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
1012 }
1013
1014 if (close) {
1015 ed.growForVerb(kClose_Verb);
1016 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
1017 }
1018
1019 DIRTY_AFTER_EDIT;
1020 SkDEBUGCODE(this->validate();)
1021 }
1022
1023 #include "SkGeometry.h"
1024
arc_is_lone_point(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,SkPoint * pt)1025 static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1026 SkPoint* pt) {
1027 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
1028 // Chrome uses this path to move into and out of ovals. If not
1029 // treated as a special case the moves can distort the oval's
1030 // bounding box (and break the circle special case).
1031 pt->set(oval.fRight, oval.centerY());
1032 return true;
1033 } else if (0 == oval.width() && 0 == oval.height()) {
1034 // Chrome will sometimes create 0 radius round rects. Having degenerate
1035 // quad segments in the path prevents the path from being recognized as
1036 // a rect.
1037 // TODO: optimizing the case where only one of width or height is zero
1038 // should also be considered. This case, however, doesn't seem to be
1039 // as common as the single point case.
1040 pt->set(oval.fRight, oval.fTop);
1041 return true;
1042 }
1043 return false;
1044 }
1045
1046 // Return the unit vectors pointing at the start/stop points for the given start/sweep angles
1047 //
angles_to_unit_vectors(SkScalar startAngle,SkScalar sweepAngle,SkVector * startV,SkVector * stopV,SkRotationDirection * dir)1048 static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
1049 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
1050 startV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &startV->fX);
1051 stopV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), &stopV->fX);
1052
1053 /* If the sweep angle is nearly (but less than) 360, then due to precision
1054 loss in radians-conversion and/or sin/cos, we may end up with coincident
1055 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
1056 of drawing a nearly complete circle (good).
1057 e.g. canvas.drawArc(0, 359.99, ...)
1058 -vs- canvas.drawArc(0, 359.9, ...)
1059 We try to detect this edge case, and tweak the stop vector
1060 */
1061 if (*startV == *stopV) {
1062 SkScalar sw = SkScalarAbs(sweepAngle);
1063 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
1064 SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
1065 // make a guess at a tiny angle (in radians) to tweak by
1066 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
1067 // not sure how much will be enough, so we use a loop
1068 do {
1069 stopRad -= deltaRad;
1070 stopV->fY = SkScalarSinCos(stopRad, &stopV->fX);
1071 } while (*startV == *stopV);
1072 }
1073 }
1074 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
1075 }
1076
1077 /**
1078 * If this returns 0, then the caller should just line-to the singlePt, else it should
1079 * ignore singlePt and append the specified number of conics.
1080 */
build_arc_conics(const SkRect & oval,const SkVector & start,const SkVector & stop,SkRotationDirection dir,SkConic conics[SkConic::kMaxConicsForArc],SkPoint * singlePt)1081 static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
1082 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
1083 SkPoint* singlePt) {
1084 SkMatrix matrix;
1085
1086 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
1087 matrix.postTranslate(oval.centerX(), oval.centerY());
1088
1089 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
1090 if (0 == count) {
1091 matrix.mapXY(start.x(), start.y(), singlePt);
1092 }
1093 return count;
1094 }
1095
addRoundRect(const SkRect & rect,const SkScalar radii[],Direction dir)1096 void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
1097 Direction dir) {
1098 SkRRect rrect;
1099 rrect.setRectRadii(rect, (const SkVector*) radii);
1100 this->addRRect(rrect, dir);
1101 }
1102
addRRect(const SkRRect & rrect,Direction dir)1103 void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
1104 // legacy start indices: 6 (CW) and 7(CCW)
1105 this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
1106 }
1107
addRRect(const SkRRect & rrect,Direction dir,unsigned startIndex)1108 void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
1109 assert_known_direction(dir);
1110
1111 if (rrect.isEmpty()) {
1112 return;
1113 }
1114
1115 bool isRRect = hasOnlyMoveTos();
1116 const SkRect& bounds = rrect.getBounds();
1117
1118 if (rrect.isRect()) {
1119 // degenerate(rect) => radii points are collapsing
1120 this->addRect(bounds, dir, (startIndex + 1) / 2);
1121 } else if (rrect.isOval()) {
1122 // degenerate(oval) => line points are collapsing
1123 this->addOval(bounds, dir, startIndex / 2);
1124 } else {
1125 fFirstDirection = this->hasOnlyMoveTos() ?
1126 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
1127
1128 SkAutoPathBoundsUpdate apbu(this, bounds);
1129 SkAutoDisableDirectionCheck addc(this);
1130
1131 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
1132 const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
1133 const SkScalar weight = SK_ScalarRoot2Over2;
1134
1135 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1136 const int kVerbs = startsWithConic
1137 ? 9 // moveTo + 4x conicTo + 3x lineTo + close
1138 : 10; // moveTo + 4x lineTo + 4x conicTo + close
1139 this->incReserve(kVerbs);
1140
1141 RRectPointIterator rrectIter(rrect, dir, startIndex);
1142 // Corner iterator indices follow the collapsed radii model,
1143 // adjusted such that the start pt is "behind" the radii start pt.
1144 const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
1145 RectPointIterator rectIter(bounds, dir, rectStartIndex);
1146
1147 this->moveTo(rrectIter.current());
1148 if (startsWithConic) {
1149 for (unsigned i = 0; i < 3; ++i) {
1150 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1151 this->lineTo(rrectIter.next());
1152 }
1153 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1154 // final lineTo handled by close().
1155 } else {
1156 for (unsigned i = 0; i < 4; ++i) {
1157 this->lineTo(rrectIter.next());
1158 this->conicTo(rectIter.next(), rrectIter.next(), weight);
1159 }
1160 }
1161 this->close();
1162
1163 SkPathRef::Editor ed(&fPathRef);
1164 ed.setIsRRect(isRRect);
1165
1166 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1167 }
1168
1169 SkDEBUGCODE(fPathRef->validate();)
1170 }
1171
hasOnlyMoveTos() const1172 bool SkPath::hasOnlyMoveTos() const {
1173 int count = fPathRef->countVerbs();
1174 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
1175 for (int i = 0; i < count; ++i) {
1176 if (*verbs == kLine_Verb ||
1177 *verbs == kQuad_Verb ||
1178 *verbs == kConic_Verb ||
1179 *verbs == kCubic_Verb) {
1180 return false;
1181 }
1182 ++verbs;
1183 }
1184 return true;
1185 }
1186
isZeroLength() const1187 bool SkPath::isZeroLength() const {
1188 int count = fPathRef->countPoints();
1189 if (count < 2) {
1190 return true;
1191 }
1192 const SkPoint* pts = fPathRef.get()->points();
1193 const SkPoint& first = *pts;
1194 for (int index = 1; index < count; ++index) {
1195 if (first != pts[index]) {
1196 return false;
1197 }
1198 }
1199 return true;
1200 }
1201
addRoundRect(const SkRect & rect,SkScalar rx,SkScalar ry,Direction dir)1202 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1203 Direction dir) {
1204 assert_known_direction(dir);
1205
1206 if (rx < 0 || ry < 0) {
1207 SkErrorInternals::SetError( kInvalidArgument_SkError,
1208 "I got %f and %f as radii to SkPath::AddRoundRect, "
1209 "but negative radii are not allowed.",
1210 SkScalarToDouble(rx), SkScalarToDouble(ry) );
1211 return;
1212 }
1213
1214 SkRRect rrect;
1215 rrect.setRectXY(rect, rx, ry);
1216 this->addRRect(rrect, dir);
1217 }
1218
addOval(const SkRect & oval,Direction dir)1219 void SkPath::addOval(const SkRect& oval, Direction dir) {
1220 // legacy start index: 1
1221 this->addOval(oval, dir, 1);
1222 }
1223
addOval(const SkRect & oval,Direction dir,unsigned startPointIndex)1224 void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
1225 assert_known_direction(dir);
1226
1227 /* If addOval() is called after previous moveTo(),
1228 this path is still marked as an oval. This is used to
1229 fit into WebKit's calling sequences.
1230 We can't simply check isEmpty() in this case, as additional
1231 moveTo() would mark the path non empty.
1232 */
1233 bool isOval = hasOnlyMoveTos();
1234 if (isOval) {
1235 fFirstDirection = (SkPathPriv::FirstDirection)dir;
1236 } else {
1237 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1238 }
1239
1240 SkAutoDisableDirectionCheck addc(this);
1241 SkAutoPathBoundsUpdate apbu(this, oval);
1242
1243 SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1244 const int kVerbs = 6; // moveTo + 4x conicTo + close
1245 this->incReserve(kVerbs);
1246
1247 OvalPointIterator ovalIter(oval, dir, startPointIndex);
1248 // The corner iterator pts are tracking "behind" the oval/radii pts.
1249 RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
1250 const SkScalar weight = SK_ScalarRoot2Over2;
1251
1252 this->moveTo(ovalIter.current());
1253 for (unsigned i = 0; i < 4; ++i) {
1254 this->conicTo(rectIter.next(), ovalIter.next(), weight);
1255 }
1256 this->close();
1257
1258 SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1259
1260 SkPathRef::Editor ed(&fPathRef);
1261
1262 ed.setIsOval(isOval);
1263 }
1264
addCircle(SkScalar x,SkScalar y,SkScalar r,Direction dir)1265 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
1266 if (r > 0) {
1267 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
1268 }
1269 }
1270
arcTo(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool forceMoveTo)1271 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1272 bool forceMoveTo) {
1273 if (oval.width() < 0 || oval.height() < 0) {
1274 return;
1275 }
1276
1277 if (fPathRef->countVerbs() == 0) {
1278 forceMoveTo = true;
1279 }
1280
1281 SkPoint lonePt;
1282 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1283 forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1284 return;
1285 }
1286
1287 SkVector startV, stopV;
1288 SkRotationDirection dir;
1289 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1290
1291 SkPoint singlePt;
1292 SkConic conics[SkConic::kMaxConicsForArc];
1293 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1294 if (count) {
1295 this->incReserve(count * 2 + 1);
1296 const SkPoint& pt = conics[0].fPts[0];
1297 forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
1298 for (int i = 0; i < count; ++i) {
1299 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1300 }
1301 } else {
1302 forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
1303 }
1304 }
1305
1306 // This converts the SVG arc to conics.
1307 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
1308 // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
1309 // See also SVG implementation notes:
1310 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
1311 // 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)1312 void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
1313 SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
1314 this->injectMoveToIfNeeded();
1315 SkPoint srcPts[2];
1316 this->getLastPt(&srcPts[0]);
1317 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
1318 // joining the endpoints.
1319 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
1320 if (!rx || !ry) {
1321 this->lineTo(x, y);
1322 return;
1323 }
1324 // If the current point and target point for the arc are identical, it should be treated as a
1325 // zero length path. This ensures continuity in animations.
1326 srcPts[1].set(x, y);
1327 if (srcPts[0] == srcPts[1]) {
1328 this->lineTo(x, y);
1329 return;
1330 }
1331 rx = SkScalarAbs(rx);
1332 ry = SkScalarAbs(ry);
1333 SkVector midPointDistance = srcPts[0] - srcPts[1];
1334 midPointDistance *= 0.5f;
1335
1336 SkMatrix pointTransform;
1337 pointTransform.setRotate(-angle);
1338
1339 SkPoint transformedMidPoint;
1340 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
1341 SkScalar squareRx = rx * rx;
1342 SkScalar squareRy = ry * ry;
1343 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
1344 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
1345
1346 // Check if the radii are big enough to draw the arc, scale radii if not.
1347 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
1348 SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
1349 if (radiiScale > 1) {
1350 radiiScale = SkScalarSqrt(radiiScale);
1351 rx *= radiiScale;
1352 ry *= radiiScale;
1353 }
1354
1355 pointTransform.setScale(1 / rx, 1 / ry);
1356 pointTransform.preRotate(-angle);
1357
1358 SkPoint unitPts[2];
1359 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
1360 SkVector delta = unitPts[1] - unitPts[0];
1361
1362 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
1363 SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
1364
1365 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
1366 if (SkToBool(arcSweep) != SkToBool(arcLarge)) { // flipped from the original implementation
1367 scaleFactor = -scaleFactor;
1368 }
1369 delta.scale(scaleFactor);
1370 SkPoint centerPoint = unitPts[0] + unitPts[1];
1371 centerPoint *= 0.5f;
1372 centerPoint.offset(-delta.fY, delta.fX);
1373 unitPts[0] -= centerPoint;
1374 unitPts[1] -= centerPoint;
1375 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
1376 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
1377 SkScalar thetaArc = theta2 - theta1;
1378 if (thetaArc < 0 && !arcSweep) { // arcSweep flipped from the original implementation
1379 thetaArc += SK_ScalarPI * 2;
1380 } else if (thetaArc > 0 && arcSweep) { // arcSweep flipped from the original implementation
1381 thetaArc -= SK_ScalarPI * 2;
1382 }
1383 pointTransform.setRotate(angle);
1384 pointTransform.preScale(rx, ry);
1385
1386 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2)));
1387 SkScalar thetaWidth = thetaArc / segments;
1388 SkScalar t = SkScalarTan(0.5f * thetaWidth);
1389 if (!SkScalarIsFinite(t)) {
1390 return;
1391 }
1392 SkScalar startTheta = theta1;
1393 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
1394 for (int i = 0; i < segments; ++i) {
1395 SkScalar endTheta = startTheta + thetaWidth;
1396 SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
1397
1398 unitPts[1].set(cosEndTheta, sinEndTheta);
1399 unitPts[1] += centerPoint;
1400 unitPts[0] = unitPts[1];
1401 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
1402 SkPoint mapped[2];
1403 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
1404 this->conicTo(mapped[0], mapped[1], w);
1405 startTheta = endTheta;
1406 }
1407 }
1408
rArcTo(SkScalar rx,SkScalar ry,SkScalar xAxisRotate,SkPath::ArcSize largeArc,SkPath::Direction sweep,SkScalar dx,SkScalar dy)1409 void SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
1410 SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
1411 SkPoint currentPoint;
1412 this->getLastPt(¤tPoint);
1413 this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, currentPoint.fX + dx, currentPoint.fY + dy);
1414 }
1415
addArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle)1416 void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
1417 if (oval.isEmpty() || 0 == sweepAngle) {
1418 return;
1419 }
1420
1421 const SkScalar kFullCircleAngle = SkIntToScalar(360);
1422
1423 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
1424 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
1425 } else {
1426 this->arcTo(oval, startAngle, sweepAngle, true);
1427 }
1428 }
1429
1430 /*
1431 Need to handle the case when the angle is sharp, and our computed end-points
1432 for the arc go behind pt1 and/or p2...
1433 */
arcTo(SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar radius)1434 void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1435 if (radius == 0) {
1436 this->lineTo(x1, y1);
1437 return;
1438 }
1439
1440 SkVector before, after;
1441
1442 // need to know our prev pt so we can construct tangent vectors
1443 {
1444 SkPoint start;
1445 this->getLastPt(&start);
1446 // Handle degenerate cases by adding a line to the first point and
1447 // bailing out.
1448 before.setNormalize(x1 - start.fX, y1 - start.fY);
1449 after.setNormalize(x2 - x1, y2 - y1);
1450 }
1451
1452 SkScalar cosh = SkPoint::DotProduct(before, after);
1453 SkScalar sinh = SkPoint::CrossProduct(before, after);
1454
1455 if (SkScalarNearlyZero(sinh)) { // angle is too tight
1456 this->lineTo(x1, y1);
1457 return;
1458 }
1459
1460 SkScalar dist = SkScalarAbs(SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh));
1461
1462 SkScalar xx = x1 - SkScalarMul(dist, before.fX);
1463 SkScalar yy = y1 - SkScalarMul(dist, before.fY);
1464 after.setLength(dist);
1465 this->lineTo(xx, yy);
1466 SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf);
1467 this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
1468 }
1469
1470 ///////////////////////////////////////////////////////////////////////////////
1471
addPath(const SkPath & path,SkScalar dx,SkScalar dy,AddPathMode mode)1472 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
1473 SkMatrix matrix;
1474
1475 matrix.setTranslate(dx, dy);
1476 this->addPath(path, matrix, mode);
1477 }
1478
addPath(const SkPath & path,const SkMatrix & matrix,AddPathMode mode)1479 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix, AddPathMode mode) {
1480 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
1481
1482 RawIter iter(path);
1483 SkPoint pts[4];
1484 Verb verb;
1485
1486 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
1487 bool firstVerb = true;
1488 while ((verb = iter.next(pts)) != kDone_Verb) {
1489 switch (verb) {
1490 case kMove_Verb:
1491 proc(matrix, &pts[0], &pts[0], 1);
1492 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
1493 injectMoveToIfNeeded(); // In case last contour is closed
1494 this->lineTo(pts[0]);
1495 } else {
1496 this->moveTo(pts[0]);
1497 }
1498 break;
1499 case kLine_Verb:
1500 proc(matrix, &pts[1], &pts[1], 1);
1501 this->lineTo(pts[1]);
1502 break;
1503 case kQuad_Verb:
1504 proc(matrix, &pts[1], &pts[1], 2);
1505 this->quadTo(pts[1], pts[2]);
1506 break;
1507 case kConic_Verb:
1508 proc(matrix, &pts[1], &pts[1], 2);
1509 this->conicTo(pts[1], pts[2], iter.conicWeight());
1510 break;
1511 case kCubic_Verb:
1512 proc(matrix, &pts[1], &pts[1], 3);
1513 this->cubicTo(pts[1], pts[2], pts[3]);
1514 break;
1515 case kClose_Verb:
1516 this->close();
1517 break;
1518 default:
1519 SkDEBUGFAIL("unknown verb");
1520 }
1521 firstVerb = false;
1522 }
1523 }
1524
1525 ///////////////////////////////////////////////////////////////////////////////
1526
pts_in_verb(unsigned verb)1527 static int pts_in_verb(unsigned verb) {
1528 static const uint8_t gPtsInVerb[] = {
1529 1, // kMove
1530 1, // kLine
1531 2, // kQuad
1532 2, // kConic
1533 3, // kCubic
1534 0, // kClose
1535 0 // kDone
1536 };
1537
1538 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
1539 return gPtsInVerb[verb];
1540 }
1541
1542 // ignore the last point of the 1st contour
reversePathTo(const SkPath & path)1543 void SkPath::reversePathTo(const SkPath& path) {
1544 int i, vcount = path.fPathRef->countVerbs();
1545 // exit early if the path is empty, or just has a moveTo.
1546 if (vcount < 2) {
1547 return;
1548 }
1549
1550 SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
1551
1552 const uint8_t* verbs = path.fPathRef->verbs();
1553 const SkPoint* pts = path.fPathRef->points();
1554 const SkScalar* conicWeights = path.fPathRef->conicWeights();
1555
1556 SkASSERT(verbs[~0] == kMove_Verb);
1557 for (i = 1; i < vcount; ++i) {
1558 unsigned v = verbs[~i];
1559 int n = pts_in_verb(v);
1560 if (n == 0) {
1561 break;
1562 }
1563 pts += n;
1564 conicWeights += (SkPath::kConic_Verb == v);
1565 }
1566
1567 while (--i > 0) {
1568 switch (verbs[~i]) {
1569 case kLine_Verb:
1570 this->lineTo(pts[-1].fX, pts[-1].fY);
1571 break;
1572 case kQuad_Verb:
1573 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
1574 break;
1575 case kConic_Verb:
1576 this->conicTo(pts[-1], pts[-2], *--conicWeights);
1577 break;
1578 case kCubic_Verb:
1579 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
1580 pts[-3].fX, pts[-3].fY);
1581 break;
1582 default:
1583 SkDEBUGFAIL("bad verb");
1584 break;
1585 }
1586 pts -= pts_in_verb(verbs[~i]);
1587 }
1588 }
1589
reverseAddPath(const SkPath & src)1590 void SkPath::reverseAddPath(const SkPath& src) {
1591 SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
1592
1593 const SkPoint* pts = src.fPathRef->pointsEnd();
1594 // we will iterator through src's verbs backwards
1595 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
1596 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
1597 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
1598
1599 bool needMove = true;
1600 bool needClose = false;
1601 while (verbs < verbsEnd) {
1602 uint8_t v = *(verbs++);
1603 int n = pts_in_verb(v);
1604
1605 if (needMove) {
1606 --pts;
1607 this->moveTo(pts->fX, pts->fY);
1608 needMove = false;
1609 }
1610 pts -= n;
1611 switch (v) {
1612 case kMove_Verb:
1613 if (needClose) {
1614 this->close();
1615 needClose = false;
1616 }
1617 needMove = true;
1618 pts += 1; // so we see the point in "if (needMove)" above
1619 break;
1620 case kLine_Verb:
1621 this->lineTo(pts[0]);
1622 break;
1623 case kQuad_Verb:
1624 this->quadTo(pts[1], pts[0]);
1625 break;
1626 case kConic_Verb:
1627 this->conicTo(pts[1], pts[0], *--conicWeights);
1628 break;
1629 case kCubic_Verb:
1630 this->cubicTo(pts[2], pts[1], pts[0]);
1631 break;
1632 case kClose_Verb:
1633 needClose = true;
1634 break;
1635 default:
1636 SkDEBUGFAIL("unexpected verb");
1637 }
1638 }
1639 }
1640
1641 ///////////////////////////////////////////////////////////////////////////////
1642
offset(SkScalar dx,SkScalar dy,SkPath * dst) const1643 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
1644 SkMatrix matrix;
1645
1646 matrix.setTranslate(dx, dy);
1647 this->transform(matrix, dst);
1648 }
1649
subdivide_cubic_to(SkPath * path,const SkPoint pts[4],int level=2)1650 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
1651 int level = 2) {
1652 if (--level >= 0) {
1653 SkPoint tmp[7];
1654
1655 SkChopCubicAtHalf(pts, tmp);
1656 subdivide_cubic_to(path, &tmp[0], level);
1657 subdivide_cubic_to(path, &tmp[3], level);
1658 } else {
1659 path->cubicTo(pts[1], pts[2], pts[3]);
1660 }
1661 }
1662
transform(const SkMatrix & matrix,SkPath * dst) const1663 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
1664 SkDEBUGCODE(this->validate();)
1665 if (dst == nullptr) {
1666 dst = (SkPath*)this;
1667 }
1668
1669 if (matrix.hasPerspective()) {
1670 SkPath tmp;
1671 tmp.fFillType = fFillType;
1672
1673 SkPath::Iter iter(*this, false);
1674 SkPoint pts[4];
1675 SkPath::Verb verb;
1676
1677 while ((verb = iter.next(pts, false)) != kDone_Verb) {
1678 switch (verb) {
1679 case kMove_Verb:
1680 tmp.moveTo(pts[0]);
1681 break;
1682 case kLine_Verb:
1683 tmp.lineTo(pts[1]);
1684 break;
1685 case kQuad_Verb:
1686 // promote the quad to a conic
1687 tmp.conicTo(pts[1], pts[2],
1688 SkConic::TransformW(pts, SK_Scalar1, matrix));
1689 break;
1690 case kConic_Verb:
1691 tmp.conicTo(pts[1], pts[2],
1692 SkConic::TransformW(pts, iter.conicWeight(), matrix));
1693 break;
1694 case kCubic_Verb:
1695 subdivide_cubic_to(&tmp, pts);
1696 break;
1697 case kClose_Verb:
1698 tmp.close();
1699 break;
1700 default:
1701 SkDEBUGFAIL("unknown verb");
1702 break;
1703 }
1704 }
1705
1706 dst->swap(tmp);
1707 SkPathRef::Editor ed(&dst->fPathRef);
1708 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
1709 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1710 } else {
1711 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
1712
1713 if (this != dst) {
1714 dst->fFillType = fFillType;
1715 dst->fConvexity = fConvexity;
1716 dst->fIsVolatile = fIsVolatile;
1717 }
1718
1719 if (SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
1720 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1721 } else {
1722 SkScalar det2x2 =
1723 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) -
1724 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY));
1725 if (det2x2 < 0) {
1726 dst->fFirstDirection = SkPathPriv::OppositeFirstDirection(
1727 (SkPathPriv::FirstDirection)fFirstDirection.load());
1728 } else if (det2x2 > 0) {
1729 dst->fFirstDirection = fFirstDirection.load();
1730 } else {
1731 dst->fConvexity = kUnknown_Convexity;
1732 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1733 }
1734 }
1735
1736 SkDEBUGCODE(dst->validate();)
1737 }
1738 }
1739
1740 ///////////////////////////////////////////////////////////////////////////////
1741 ///////////////////////////////////////////////////////////////////////////////
1742
1743 enum SegmentState {
1744 kEmptyContour_SegmentState, // The current contour is empty. We may be
1745 // starting processing or we may have just
1746 // closed a contour.
1747 kAfterMove_SegmentState, // We have seen a move, but nothing else.
1748 kAfterPrimitive_SegmentState // We have seen a primitive but not yet
1749 // closed the path. Also the initial state.
1750 };
1751
Iter()1752 SkPath::Iter::Iter() {
1753 #ifdef SK_DEBUG
1754 fPts = nullptr;
1755 fConicWeights = nullptr;
1756 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
1757 fForceClose = fCloseLine = false;
1758 fSegmentState = kEmptyContour_SegmentState;
1759 #endif
1760 // need to init enough to make next() harmlessly return kDone_Verb
1761 fVerbs = nullptr;
1762 fVerbStop = nullptr;
1763 fNeedClose = false;
1764 }
1765
Iter(const SkPath & path,bool forceClose)1766 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
1767 this->setPath(path, forceClose);
1768 }
1769
setPath(const SkPath & path,bool forceClose)1770 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
1771 fPts = path.fPathRef->points();
1772 fVerbs = path.fPathRef->verbs();
1773 fVerbStop = path.fPathRef->verbsMemBegin();
1774 fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
1775 fLastPt.fX = fLastPt.fY = 0;
1776 fMoveTo.fX = fMoveTo.fY = 0;
1777 fForceClose = SkToU8(forceClose);
1778 fNeedClose = false;
1779 fSegmentState = kEmptyContour_SegmentState;
1780 }
1781
isClosedContour() const1782 bool SkPath::Iter::isClosedContour() const {
1783 if (fVerbs == nullptr || fVerbs == fVerbStop) {
1784 return false;
1785 }
1786 if (fForceClose) {
1787 return true;
1788 }
1789
1790 const uint8_t* verbs = fVerbs;
1791 const uint8_t* stop = fVerbStop;
1792
1793 if (kMove_Verb == *(verbs - 1)) {
1794 verbs -= 1; // skip the initial moveto
1795 }
1796
1797 while (verbs > stop) {
1798 // verbs points one beyond the current verb, decrement first.
1799 unsigned v = *(--verbs);
1800 if (kMove_Verb == v) {
1801 break;
1802 }
1803 if (kClose_Verb == v) {
1804 return true;
1805 }
1806 }
1807 return false;
1808 }
1809
autoClose(SkPoint pts[2])1810 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
1811 SkASSERT(pts);
1812 if (fLastPt != fMoveTo) {
1813 // A special case: if both points are NaN, SkPoint::operation== returns
1814 // false, but the iterator expects that they are treated as the same.
1815 // (consider SkPoint is a 2-dimension float point).
1816 if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
1817 SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
1818 return kClose_Verb;
1819 }
1820
1821 pts[0] = fLastPt;
1822 pts[1] = fMoveTo;
1823 fLastPt = fMoveTo;
1824 fCloseLine = true;
1825 return kLine_Verb;
1826 } else {
1827 pts[0] = fMoveTo;
1828 return kClose_Verb;
1829 }
1830 }
1831
cons_moveTo()1832 const SkPoint& SkPath::Iter::cons_moveTo() {
1833 if (fSegmentState == kAfterMove_SegmentState) {
1834 // Set the first return pt to the move pt
1835 fSegmentState = kAfterPrimitive_SegmentState;
1836 return fMoveTo;
1837 } else {
1838 SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
1839 // Set the first return pt to the last pt of the previous primitive.
1840 return fPts[-1];
1841 }
1842 }
1843
consumeDegenerateSegments(bool exact)1844 void SkPath::Iter::consumeDegenerateSegments(bool exact) {
1845 // We need to step over anything that will not move the current draw point
1846 // forward before the next move is seen
1847 const uint8_t* lastMoveVerb = 0;
1848 const SkPoint* lastMovePt = 0;
1849 const SkScalar* lastMoveWeight = nullptr;
1850 SkPoint lastPt = fLastPt;
1851 while (fVerbs != fVerbStop) {
1852 unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
1853 switch (verb) {
1854 case kMove_Verb:
1855 // Keep a record of this most recent move
1856 lastMoveVerb = fVerbs;
1857 lastMovePt = fPts;
1858 lastMoveWeight = fConicWeights;
1859 lastPt = fPts[0];
1860 fVerbs--;
1861 fPts++;
1862 break;
1863
1864 case kClose_Verb:
1865 // A close when we are in a segment is always valid except when it
1866 // follows a move which follows a segment.
1867 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
1868 return;
1869 }
1870 // A close at any other time must be ignored
1871 fVerbs--;
1872 break;
1873
1874 case kLine_Verb:
1875 if (!IsLineDegenerate(lastPt, fPts[0], exact)) {
1876 if (lastMoveVerb) {
1877 fVerbs = lastMoveVerb;
1878 fPts = lastMovePt;
1879 fConicWeights = lastMoveWeight;
1880 return;
1881 }
1882 return;
1883 }
1884 // Ignore this line and continue
1885 fVerbs--;
1886 fPts++;
1887 break;
1888
1889 case kConic_Verb:
1890 case kQuad_Verb:
1891 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1], exact)) {
1892 if (lastMoveVerb) {
1893 fVerbs = lastMoveVerb;
1894 fPts = lastMovePt;
1895 fConicWeights = lastMoveWeight;
1896 return;
1897 }
1898 return;
1899 }
1900 // Ignore this line and continue
1901 fVerbs--;
1902 fPts += 2;
1903 fConicWeights += (kConic_Verb == verb);
1904 break;
1905
1906 case kCubic_Verb:
1907 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2], exact)) {
1908 if (lastMoveVerb) {
1909 fVerbs = lastMoveVerb;
1910 fPts = lastMovePt;
1911 fConicWeights = lastMoveWeight;
1912 return;
1913 }
1914 return;
1915 }
1916 // Ignore this line and continue
1917 fVerbs--;
1918 fPts += 3;
1919 break;
1920
1921 default:
1922 SkDEBUGFAIL("Should never see kDone_Verb");
1923 }
1924 }
1925 }
1926
doNext(SkPoint ptsParam[4])1927 SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
1928 SkASSERT(ptsParam);
1929
1930 if (fVerbs == fVerbStop) {
1931 // Close the curve if requested and if there is some curve to close
1932 if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
1933 if (kLine_Verb == this->autoClose(ptsParam)) {
1934 return kLine_Verb;
1935 }
1936 fNeedClose = false;
1937 return kClose_Verb;
1938 }
1939 return kDone_Verb;
1940 }
1941
1942 // fVerbs is one beyond the current verb, decrement first
1943 unsigned verb = *(--fVerbs);
1944 const SkPoint* SK_RESTRICT srcPts = fPts;
1945 SkPoint* SK_RESTRICT pts = ptsParam;
1946
1947 switch (verb) {
1948 case kMove_Verb:
1949 if (fNeedClose) {
1950 fVerbs++; // move back one verb
1951 verb = this->autoClose(pts);
1952 if (verb == kClose_Verb) {
1953 fNeedClose = false;
1954 }
1955 return (Verb)verb;
1956 }
1957 if (fVerbs == fVerbStop) { // might be a trailing moveto
1958 return kDone_Verb;
1959 }
1960 fMoveTo = *srcPts;
1961 pts[0] = *srcPts;
1962 srcPts += 1;
1963 fSegmentState = kAfterMove_SegmentState;
1964 fLastPt = fMoveTo;
1965 fNeedClose = fForceClose;
1966 break;
1967 case kLine_Verb:
1968 pts[0] = this->cons_moveTo();
1969 pts[1] = srcPts[0];
1970 fLastPt = srcPts[0];
1971 fCloseLine = false;
1972 srcPts += 1;
1973 break;
1974 case kConic_Verb:
1975 fConicWeights += 1;
1976 // fall-through
1977 case kQuad_Verb:
1978 pts[0] = this->cons_moveTo();
1979 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1980 fLastPt = srcPts[1];
1981 srcPts += 2;
1982 break;
1983 case kCubic_Verb:
1984 pts[0] = this->cons_moveTo();
1985 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1986 fLastPt = srcPts[2];
1987 srcPts += 3;
1988 break;
1989 case kClose_Verb:
1990 verb = this->autoClose(pts);
1991 if (verb == kLine_Verb) {
1992 fVerbs++; // move back one verb
1993 } else {
1994 fNeedClose = false;
1995 fSegmentState = kEmptyContour_SegmentState;
1996 }
1997 fLastPt = fMoveTo;
1998 break;
1999 }
2000 fPts = srcPts;
2001 return (Verb)verb;
2002 }
2003
2004 ///////////////////////////////////////////////////////////////////////////////
2005
2006 /*
2007 Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
2008 */
2009
writeToMemory(void * storage) const2010 size_t SkPath::writeToMemory(void* storage) const {
2011 SkDEBUGCODE(this->validate();)
2012
2013 if (nullptr == storage) {
2014 const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize();
2015 return SkAlign4(byteCount);
2016 }
2017
2018 SkWBuffer buffer(storage);
2019
2020 int32_t packed = (fConvexity << kConvexity_SerializationShift) |
2021 (fFillType << kFillType_SerializationShift) |
2022 (fFirstDirection << kDirection_SerializationShift) |
2023 (fIsVolatile << kIsVolatile_SerializationShift) |
2024 kCurrent_Version;
2025
2026 buffer.write32(packed);
2027 buffer.write32(fLastMoveToIndex);
2028
2029 fPathRef->writeToBuffer(&buffer);
2030
2031 buffer.padToAlign4();
2032 return buffer.pos();
2033 }
2034
readFromMemory(const void * storage,size_t length)2035 size_t SkPath::readFromMemory(const void* storage, size_t length) {
2036 SkRBufferWithSizeCheck buffer(storage, length);
2037
2038 int32_t packed;
2039 if (!buffer.readS32(&packed)) {
2040 return 0;
2041 }
2042
2043 unsigned version = packed & 0xFF;
2044 if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) {
2045 return 0;
2046 }
2047
2048 fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
2049 fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
2050 uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
2051 fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
2052 SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
2053 if (!pathRef) {
2054 return 0;
2055 }
2056
2057 fPathRef.reset(pathRef);
2058 SkDEBUGCODE(this->validate();)
2059 buffer.skipToAlign4();
2060
2061 // compatibility check
2062 if (version < kPathPrivFirstDirection_Version) {
2063 switch (dir) { // old values
2064 case 0:
2065 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2066 break;
2067 case 1:
2068 fFirstDirection = SkPathPriv::kCW_FirstDirection;
2069 break;
2070 case 2:
2071 fFirstDirection = SkPathPriv::kCCW_FirstDirection;
2072 break;
2073 default:
2074 SkASSERT(false);
2075 }
2076 } else {
2077 fFirstDirection = dir;
2078 }
2079
2080 return buffer.pos();
2081 }
2082
2083 ///////////////////////////////////////////////////////////////////////////////
2084
2085 #include "SkStringUtils.h"
2086 #include "SkStream.h"
2087
append_params(SkString * str,const char label[],const SkPoint pts[],int count,SkScalarAsStringType strType,SkScalar conicWeight=-1)2088 static void append_params(SkString* str, const char label[], const SkPoint pts[],
2089 int count, SkScalarAsStringType strType, SkScalar conicWeight = -1) {
2090 str->append(label);
2091 str->append("(");
2092
2093 const SkScalar* values = &pts[0].fX;
2094 count *= 2;
2095
2096 for (int i = 0; i < count; ++i) {
2097 SkAppendScalar(str, values[i], strType);
2098 if (i < count - 1) {
2099 str->append(", ");
2100 }
2101 }
2102 if (conicWeight >= 0) {
2103 str->append(", ");
2104 SkAppendScalar(str, conicWeight, strType);
2105 }
2106 str->append(");");
2107 if (kHex_SkScalarAsStringType == strType) {
2108 str->append(" // ");
2109 for (int i = 0; i < count; ++i) {
2110 SkAppendScalarDec(str, values[i]);
2111 if (i < count - 1) {
2112 str->append(", ");
2113 }
2114 }
2115 if (conicWeight >= 0) {
2116 str->append(", ");
2117 SkAppendScalarDec(str, conicWeight);
2118 }
2119 }
2120 str->append("\n");
2121 }
2122
dump(SkWStream * wStream,bool forceClose,bool dumpAsHex) const2123 void SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const {
2124 SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
2125 Iter iter(*this, forceClose);
2126 SkPoint pts[4];
2127 Verb verb;
2128
2129 if (!wStream) {
2130 SkDebugf("path: forceClose=%s\n", forceClose ? "true" : "false");
2131 }
2132 SkString builder;
2133
2134 while ((verb = iter.next(pts, false)) != kDone_Verb) {
2135 switch (verb) {
2136 case kMove_Verb:
2137 append_params(&builder, "path.moveTo", &pts[0], 1, asType);
2138 break;
2139 case kLine_Verb:
2140 append_params(&builder, "path.lineTo", &pts[1], 1, asType);
2141 break;
2142 case kQuad_Verb:
2143 append_params(&builder, "path.quadTo", &pts[1], 2, asType);
2144 break;
2145 case kConic_Verb:
2146 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
2147 break;
2148 case kCubic_Verb:
2149 append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
2150 break;
2151 case kClose_Verb:
2152 builder.append("path.close();\n");
2153 break;
2154 default:
2155 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
2156 verb = kDone_Verb; // stop the loop
2157 break;
2158 }
2159 if (!wStream && builder.size()) {
2160 SkDebugf("%s", builder.c_str());
2161 builder.reset();
2162 }
2163 }
2164 if (wStream) {
2165 wStream->writeText(builder.c_str());
2166 }
2167 }
2168
dump() const2169 void SkPath::dump() const {
2170 this->dump(nullptr, false, false);
2171 }
2172
dumpHex() const2173 void SkPath::dumpHex() const {
2174 this->dump(nullptr, false, true);
2175 }
2176
2177 #ifdef SK_DEBUG
validate() const2178 void SkPath::validate() const {
2179 SkASSERT((fFillType & ~3) == 0);
2180
2181 #ifdef SK_DEBUG_PATH
2182 if (!fBoundsIsDirty) {
2183 SkRect bounds;
2184
2185 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
2186 SkASSERT(SkToBool(fIsFinite) == isFinite);
2187
2188 if (fPathRef->countPoints() <= 1) {
2189 // if we're empty, fBounds may be empty but translated, so we can't
2190 // necessarily compare to bounds directly
2191 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2192 // be [2, 2, 2, 2]
2193 SkASSERT(bounds.isEmpty());
2194 SkASSERT(fBounds.isEmpty());
2195 } else {
2196 if (bounds.isEmpty()) {
2197 SkASSERT(fBounds.isEmpty());
2198 } else {
2199 if (!fBounds.isEmpty()) {
2200 SkASSERT(fBounds.contains(bounds));
2201 }
2202 }
2203 }
2204 }
2205 #endif // SK_DEBUG_PATH
2206 }
2207 #endif // SK_DEBUG
2208
2209 ///////////////////////////////////////////////////////////////////////////////
2210
sign(SkScalar x)2211 static int sign(SkScalar x) { return x < 0; }
2212 #define kValueNeverReturnedBySign 2
2213
2214 enum DirChange {
2215 kLeft_DirChange,
2216 kRight_DirChange,
2217 kStraight_DirChange,
2218 kBackwards_DirChange,
2219
2220 kInvalid_DirChange
2221 };
2222
2223
almost_equal(SkScalar compA,SkScalar compB)2224 static bool almost_equal(SkScalar compA, SkScalar compB) {
2225 // The error epsilon was empirically derived; worse case round rects
2226 // with a mid point outset by 2x float epsilon in tests had an error
2227 // of 12.
2228 const int epsilon = 16;
2229 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2230 return false;
2231 }
2232 // no need to check for small numbers because SkPath::Iter has removed degenerate values
2233 int aBits = SkFloatAs2sCompliment(compA);
2234 int bBits = SkFloatAs2sCompliment(compB);
2235 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2236 }
2237
approximately_zero_when_compared_to(double x,double y)2238 static bool approximately_zero_when_compared_to(double x, double y) {
2239 return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
2240 }
2241
2242
2243 // only valid for a single contour
2244 struct Convexicator {
ConvexicatorConvexicator2245 Convexicator()
2246 : fPtCount(0)
2247 , fConvexity(SkPath::kConvex_Convexity)
2248 , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
2249 , fIsFinite(true)
2250 , fIsCurve(false) {
2251 fExpectedDir = kInvalid_DirChange;
2252 // warnings
2253 fLastPt.set(0, 0);
2254 fCurrPt.set(0, 0);
2255 fLastVec.set(0, 0);
2256 fFirstVec.set(0, 0);
2257
2258 fDx = fDy = 0;
2259 fSx = fSy = kValueNeverReturnedBySign;
2260 }
2261
getConvexityConvexicator2262 SkPath::Convexity getConvexity() const { return fConvexity; }
2263
2264 /** The direction returned is only valid if the path is determined convex */
getFirstDirectionConvexicator2265 SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
2266
addPtConvexicator2267 void addPt(const SkPoint& pt) {
2268 if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
2269 return;
2270 }
2271
2272 if (0 == fPtCount) {
2273 fCurrPt = pt;
2274 ++fPtCount;
2275 } else {
2276 SkVector vec = pt - fCurrPt;
2277 SkScalar lengthSqd = vec.lengthSqd();
2278 if (!SkScalarIsFinite(lengthSqd)) {
2279 fIsFinite = false;
2280 } else if (lengthSqd) {
2281 fPriorPt = fLastPt;
2282 fLastPt = fCurrPt;
2283 fCurrPt = pt;
2284 if (++fPtCount == 2) {
2285 fFirstVec = fLastVec = vec;
2286 } else {
2287 SkASSERT(fPtCount > 2);
2288 this->addVec(vec);
2289 }
2290
2291 int sx = sign(vec.fX);
2292 int sy = sign(vec.fY);
2293 fDx += (sx != fSx);
2294 fDy += (sy != fSy);
2295 fSx = sx;
2296 fSy = sy;
2297
2298 if (fDx > 3 || fDy > 3) {
2299 fConvexity = SkPath::kConcave_Convexity;
2300 }
2301 }
2302 }
2303 }
2304
closeConvexicator2305 void close() {
2306 if (fPtCount > 2) {
2307 this->addVec(fFirstVec);
2308 }
2309 }
2310
directionChangeConvexicator2311 DirChange directionChange(const SkVector& curVec) {
2312 SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2313
2314 SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2315 SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2316 largest = SkTMax(largest, -smallest);
2317
2318 if (!almost_equal(largest, largest + cross)) {
2319 int sign = SkScalarSignAsInt(cross);
2320 if (sign) {
2321 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2322 }
2323 }
2324
2325 if (cross) {
2326 double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
2327 double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
2328 double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
2329 double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
2330 double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
2331 if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
2332 int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
2333 if (sign) {
2334 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2335 }
2336 }
2337 }
2338
2339 if (!SkScalarNearlyZero(fLastVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2340 !SkScalarNearlyZero(curVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2341 fLastVec.dot(curVec) < 0.0f) {
2342 return kBackwards_DirChange;
2343 }
2344
2345 return kStraight_DirChange;
2346 }
2347
2348
isFiniteConvexicator2349 bool isFinite() const {
2350 return fIsFinite;
2351 }
2352
setCurveConvexicator2353 void setCurve(bool isCurve) {
2354 fIsCurve = isCurve;
2355 }
2356
2357 private:
addVecConvexicator2358 void addVec(const SkVector& vec) {
2359 SkASSERT(vec.fX || vec.fY);
2360 DirChange dir = this->directionChange(vec);
2361 switch (dir) {
2362 case kLeft_DirChange: // fall through
2363 case kRight_DirChange:
2364 if (kInvalid_DirChange == fExpectedDir) {
2365 fExpectedDir = dir;
2366 fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2367 : SkPathPriv::kCCW_FirstDirection;
2368 } else if (dir != fExpectedDir) {
2369 fConvexity = SkPath::kConcave_Convexity;
2370 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2371 }
2372 fLastVec = vec;
2373 break;
2374 case kStraight_DirChange:
2375 break;
2376 case kBackwards_DirChange:
2377 if (fIsCurve) {
2378 fConvexity = SkPath::kConcave_Convexity;
2379 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2380 }
2381 fLastVec = vec;
2382 break;
2383 case kInvalid_DirChange:
2384 SkFAIL("Use of invalid direction change flag");
2385 break;
2386 }
2387 }
2388
2389 SkPoint fPriorPt;
2390 SkPoint fLastPt;
2391 SkPoint fCurrPt;
2392 // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
2393 // value with the current vec is deemed to be of a significant value.
2394 SkVector fLastVec, fFirstVec;
2395 int fPtCount; // non-degenerate points
2396 DirChange fExpectedDir;
2397 SkPath::Convexity fConvexity;
2398 SkPathPriv::FirstDirection fFirstDirection;
2399 int fDx, fDy, fSx, fSy;
2400 bool fIsFinite;
2401 bool fIsCurve;
2402 };
2403
internalGetConvexity() const2404 SkPath::Convexity SkPath::internalGetConvexity() const {
2405 SkASSERT(kUnknown_Convexity == fConvexity);
2406 SkPoint pts[4];
2407 SkPath::Verb verb;
2408 SkPath::Iter iter(*this, true);
2409
2410 int contourCount = 0;
2411 int count;
2412 Convexicator state;
2413
2414 if (!isFinite()) {
2415 return kUnknown_Convexity;
2416 }
2417 while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) {
2418 switch (verb) {
2419 case kMove_Verb:
2420 if (++contourCount > 1) {
2421 fConvexity = kConcave_Convexity;
2422 return kConcave_Convexity;
2423 }
2424 pts[1] = pts[0];
2425 // fall through
2426 case kLine_Verb:
2427 count = 1;
2428 state.setCurve(false);
2429 break;
2430 case kQuad_Verb:
2431 // fall through
2432 case kConic_Verb:
2433 // fall through
2434 case kCubic_Verb:
2435 count = 2 + (kCubic_Verb == verb);
2436 // As an additional enhancement, this could set curve true only
2437 // if the curve is nonlinear
2438 state.setCurve(true);
2439 break;
2440 case kClose_Verb:
2441 state.setCurve(false);
2442 state.close();
2443 count = 0;
2444 break;
2445 default:
2446 SkDEBUGFAIL("bad verb");
2447 fConvexity = kConcave_Convexity;
2448 return kConcave_Convexity;
2449 }
2450
2451 for (int i = 1; i <= count; i++) {
2452 state.addPt(pts[i]);
2453 }
2454 // early exit
2455 if (!state.isFinite()) {
2456 return kUnknown_Convexity;
2457 }
2458 if (kConcave_Convexity == state.getConvexity()) {
2459 fConvexity = kConcave_Convexity;
2460 return kConcave_Convexity;
2461 }
2462 }
2463 fConvexity = state.getConvexity();
2464 if (kConvex_Convexity == fConvexity && SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
2465 fFirstDirection = state.getFirstDirection();
2466 }
2467 return static_cast<Convexity>(fConvexity);
2468 }
2469
2470 ///////////////////////////////////////////////////////////////////////////////
2471
2472 class ContourIter {
2473 public:
2474 ContourIter(const SkPathRef& pathRef);
2475
done() const2476 bool done() const { return fDone; }
2477 // if !done() then these may be called
count() const2478 int count() const { return fCurrPtCount; }
pts() const2479 const SkPoint* pts() const { return fCurrPt; }
2480 void next();
2481
2482 private:
2483 int fCurrPtCount;
2484 const SkPoint* fCurrPt;
2485 const uint8_t* fCurrVerb;
2486 const uint8_t* fStopVerbs;
2487 const SkScalar* fCurrConicWeight;
2488 bool fDone;
2489 SkDEBUGCODE(int fContourCounter;)
2490 };
2491
ContourIter(const SkPathRef & pathRef)2492 ContourIter::ContourIter(const SkPathRef& pathRef) {
2493 fStopVerbs = pathRef.verbsMemBegin();
2494 fDone = false;
2495 fCurrPt = pathRef.points();
2496 fCurrVerb = pathRef.verbs();
2497 fCurrConicWeight = pathRef.conicWeights();
2498 fCurrPtCount = 0;
2499 SkDEBUGCODE(fContourCounter = 0;)
2500 this->next();
2501 }
2502
next()2503 void ContourIter::next() {
2504 if (fCurrVerb <= fStopVerbs) {
2505 fDone = true;
2506 }
2507 if (fDone) {
2508 return;
2509 }
2510
2511 // skip pts of prev contour
2512 fCurrPt += fCurrPtCount;
2513
2514 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
2515 int ptCount = 1; // moveTo
2516 const uint8_t* verbs = fCurrVerb;
2517
2518 for (--verbs; verbs > fStopVerbs; --verbs) {
2519 switch (verbs[~0]) {
2520 case SkPath::kMove_Verb:
2521 goto CONTOUR_END;
2522 case SkPath::kLine_Verb:
2523 ptCount += 1;
2524 break;
2525 case SkPath::kConic_Verb:
2526 fCurrConicWeight += 1;
2527 // fall-through
2528 case SkPath::kQuad_Verb:
2529 ptCount += 2;
2530 break;
2531 case SkPath::kCubic_Verb:
2532 ptCount += 3;
2533 break;
2534 case SkPath::kClose_Verb:
2535 break;
2536 default:
2537 SkDEBUGFAIL("unexpected verb");
2538 break;
2539 }
2540 }
2541 CONTOUR_END:
2542 fCurrPtCount = ptCount;
2543 fCurrVerb = verbs;
2544 SkDEBUGCODE(++fContourCounter;)
2545 }
2546
2547 // returns cross product of (p1 - p0) and (p2 - p0)
cross_prod(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2)2548 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2549 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2550 // We may get 0 when the above subtracts underflow. We expect this to be
2551 // very rare and lazily promote to double.
2552 if (0 == cross) {
2553 double p0x = SkScalarToDouble(p0.fX);
2554 double p0y = SkScalarToDouble(p0.fY);
2555
2556 double p1x = SkScalarToDouble(p1.fX);
2557 double p1y = SkScalarToDouble(p1.fY);
2558
2559 double p2x = SkScalarToDouble(p2.fX);
2560 double p2y = SkScalarToDouble(p2.fY);
2561
2562 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
2563 (p1y - p0y) * (p2x - p0x));
2564
2565 }
2566 return cross;
2567 }
2568
2569 // Returns the first pt with the maximum Y coordinate
find_max_y(const SkPoint pts[],int count)2570 static int find_max_y(const SkPoint pts[], int count) {
2571 SkASSERT(count > 0);
2572 SkScalar max = pts[0].fY;
2573 int firstIndex = 0;
2574 for (int i = 1; i < count; ++i) {
2575 SkScalar y = pts[i].fY;
2576 if (y > max) {
2577 max = y;
2578 firstIndex = i;
2579 }
2580 }
2581 return firstIndex;
2582 }
2583
find_diff_pt(const SkPoint pts[],int index,int n,int inc)2584 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
2585 int i = index;
2586 for (;;) {
2587 i = (i + inc) % n;
2588 if (i == index) { // we wrapped around, so abort
2589 break;
2590 }
2591 if (pts[index] != pts[i]) { // found a different point, success!
2592 break;
2593 }
2594 }
2595 return i;
2596 }
2597
2598 /**
2599 * Starting at index, and moving forward (incrementing), find the xmin and
2600 * xmax of the contiguous points that have the same Y.
2601 */
find_min_max_x_at_y(const SkPoint pts[],int index,int n,int * maxIndexPtr)2602 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
2603 int* maxIndexPtr) {
2604 const SkScalar y = pts[index].fY;
2605 SkScalar min = pts[index].fX;
2606 SkScalar max = min;
2607 int minIndex = index;
2608 int maxIndex = index;
2609 for (int i = index + 1; i < n; ++i) {
2610 if (pts[i].fY != y) {
2611 break;
2612 }
2613 SkScalar x = pts[i].fX;
2614 if (x < min) {
2615 min = x;
2616 minIndex = i;
2617 } else if (x > max) {
2618 max = x;
2619 maxIndex = i;
2620 }
2621 }
2622 *maxIndexPtr = maxIndex;
2623 return minIndex;
2624 }
2625
crossToDir(SkScalar cross,SkPathPriv::FirstDirection * dir)2626 static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
2627 *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
2628 }
2629
2630 /*
2631 * We loop through all contours, and keep the computed cross-product of the
2632 * contour that contained the global y-max. If we just look at the first
2633 * contour, we may find one that is wound the opposite way (correctly) since
2634 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
2635 * that is outer most (or at least has the global y-max) before we can consider
2636 * its cross product.
2637 */
CheapComputeFirstDirection(const SkPath & path,FirstDirection * dir)2638 bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
2639 if (kUnknown_FirstDirection != path.fFirstDirection.load()) {
2640 *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
2641 return true;
2642 }
2643
2644 // don't want to pay the cost for computing this if it
2645 // is unknown, so we don't call isConvex()
2646 if (SkPath::kConvex_Convexity == path.getConvexityOrUnknown()) {
2647 SkASSERT(kUnknown_FirstDirection == path.fFirstDirection);
2648 *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
2649 return false;
2650 }
2651
2652 ContourIter iter(*path.fPathRef.get());
2653
2654 // initialize with our logical y-min
2655 SkScalar ymax = path.getBounds().fTop;
2656 SkScalar ymaxCross = 0;
2657
2658 for (; !iter.done(); iter.next()) {
2659 int n = iter.count();
2660 if (n < 3) {
2661 continue;
2662 }
2663
2664 const SkPoint* pts = iter.pts();
2665 SkScalar cross = 0;
2666 int index = find_max_y(pts, n);
2667 if (pts[index].fY < ymax) {
2668 continue;
2669 }
2670
2671 // If there is more than 1 distinct point at the y-max, we take the
2672 // x-min and x-max of them and just subtract to compute the dir.
2673 if (pts[(index + 1) % n].fY == pts[index].fY) {
2674 int maxIndex;
2675 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2676 if (minIndex == maxIndex) {
2677 goto TRY_CROSSPROD;
2678 }
2679 SkASSERT(pts[minIndex].fY == pts[index].fY);
2680 SkASSERT(pts[maxIndex].fY == pts[index].fY);
2681 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2682 // we just subtract the indices, and let that auto-convert to
2683 // SkScalar, since we just want - or + to signal the direction.
2684 cross = minIndex - maxIndex;
2685 } else {
2686 TRY_CROSSPROD:
2687 // Find a next and prev index to use for the cross-product test,
2688 // but we try to find pts that form non-zero vectors from pts[index]
2689 //
2690 // Its possible that we can't find two non-degenerate vectors, so
2691 // we have to guard our search (e.g. all the pts could be in the
2692 // same place).
2693
2694 // we pass n - 1 instead of -1 so we don't foul up % operator by
2695 // passing it a negative LH argument.
2696 int prev = find_diff_pt(pts, index, n, n - 1);
2697 if (prev == index) {
2698 // completely degenerate, skip to next contour
2699 continue;
2700 }
2701 int next = find_diff_pt(pts, index, n, 1);
2702 SkASSERT(next != index);
2703 cross = cross_prod(pts[prev], pts[index], pts[next]);
2704 // if we get a zero and the points are horizontal, then we look at the spread in
2705 // x-direction. We really should continue to walk away from the degeneracy until
2706 // there is a divergence.
2707 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
2708 // construct the subtract so we get the correct Direction below
2709 cross = pts[index].fX - pts[next].fX;
2710 }
2711 }
2712
2713 if (cross) {
2714 // record our best guess so far
2715 ymax = pts[index].fY;
2716 ymaxCross = cross;
2717 }
2718 }
2719 if (ymaxCross) {
2720 crossToDir(ymaxCross, dir);
2721 path.fFirstDirection = *dir;
2722 return true;
2723 } else {
2724 return false;
2725 }
2726 }
2727
2728 ///////////////////////////////////////////////////////////////////////////////
2729
between(SkScalar a,SkScalar b,SkScalar c)2730 static bool between(SkScalar a, SkScalar b, SkScalar c) {
2731 SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
2732 || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
2733 return (a - b) * (c - b) <= 0;
2734 }
2735
eval_cubic_coeff(SkScalar A,SkScalar B,SkScalar C,SkScalar D,SkScalar t)2736 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
2737 SkScalar D, SkScalar t) {
2738 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
2739 }
2740
eval_cubic_pts(SkScalar c0,SkScalar c1,SkScalar c2,SkScalar c3,SkScalar t)2741 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
2742 SkScalar t) {
2743 SkScalar A = c3 + 3*(c1 - c2) - c0;
2744 SkScalar B = 3*(c2 - c1 - c1 + c0);
2745 SkScalar C = 3*(c1 - c0);
2746 SkScalar D = c0;
2747 return eval_cubic_coeff(A, B, C, D, t);
2748 }
2749
find_minmax(const SkPoint pts[],SkScalar * minPtr,SkScalar * maxPtr)2750 template <size_t N> static void find_minmax(const SkPoint pts[],
2751 SkScalar* minPtr, SkScalar* maxPtr) {
2752 SkScalar min, max;
2753 min = max = pts[0].fX;
2754 for (size_t i = 1; i < N; ++i) {
2755 min = SkMinScalar(min, pts[i].fX);
2756 max = SkMaxScalar(max, pts[i].fX);
2757 }
2758 *minPtr = min;
2759 *maxPtr = max;
2760 }
2761
checkOnCurve(SkScalar x,SkScalar y,const SkPoint & start,const SkPoint & end)2762 static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
2763 if (start.fY == end.fY) {
2764 return between(start.fX, x, end.fX) && x != end.fX;
2765 } else {
2766 return x == start.fX && y == start.fY;
2767 }
2768 }
2769
winding_mono_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2770 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2771 SkScalar y0 = pts[0].fY;
2772 SkScalar y3 = pts[3].fY;
2773
2774 int dir = 1;
2775 if (y0 > y3) {
2776 SkTSwap(y0, y3);
2777 dir = -1;
2778 }
2779 if (y < y0 || y > y3) {
2780 return 0;
2781 }
2782 if (checkOnCurve(x, y, pts[0], pts[3])) {
2783 *onCurveCount += 1;
2784 return 0;
2785 }
2786 if (y == y3) {
2787 return 0;
2788 }
2789
2790 // quickreject or quickaccept
2791 SkScalar min, max;
2792 find_minmax<4>(pts, &min, &max);
2793 if (x < min) {
2794 return 0;
2795 }
2796 if (x > max) {
2797 return dir;
2798 }
2799
2800 // compute the actual x(t) value
2801 SkScalar t;
2802 SkAssertResult(SkCubicClipper::ChopMonoAtY(pts, y, &t));
2803 SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
2804 if (SkScalarNearlyEqual(xt, x)) {
2805 if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points
2806 *onCurveCount += 1;
2807 return 0;
2808 }
2809 }
2810 return xt < x ? dir : 0;
2811 }
2812
winding_cubic(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2813 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2814 SkPoint dst[10];
2815 int n = SkChopCubicAtYExtrema(pts, dst);
2816 int w = 0;
2817 for (int i = 0; i <= n; ++i) {
2818 w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
2819 }
2820 return w;
2821 }
2822
conic_eval_numerator(const SkScalar src[],SkScalar w,SkScalar t)2823 static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
2824 SkASSERT(src);
2825 SkASSERT(t >= 0 && t <= 1);
2826 SkScalar src2w = src[2] * w;
2827 SkScalar C = src[0];
2828 SkScalar A = src[4] - 2 * src2w + C;
2829 SkScalar B = 2 * (src2w - C);
2830 return (A * t + B) * t + C;
2831 }
2832
2833
conic_eval_denominator(SkScalar w,SkScalar t)2834 static double conic_eval_denominator(SkScalar w, SkScalar t) {
2835 SkScalar B = 2 * (w - 1);
2836 SkScalar C = 1;
2837 SkScalar A = -B;
2838 return (A * t + B) * t + C;
2839 }
2840
winding_mono_conic(const SkConic & conic,SkScalar x,SkScalar y,int * onCurveCount)2841 static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
2842 const SkPoint* pts = conic.fPts;
2843 SkScalar y0 = pts[0].fY;
2844 SkScalar y2 = pts[2].fY;
2845
2846 int dir = 1;
2847 if (y0 > y2) {
2848 SkTSwap(y0, y2);
2849 dir = -1;
2850 }
2851 if (y < y0 || y > y2) {
2852 return 0;
2853 }
2854 if (checkOnCurve(x, y, pts[0], pts[2])) {
2855 *onCurveCount += 1;
2856 return 0;
2857 }
2858 if (y == y2) {
2859 return 0;
2860 }
2861
2862 SkScalar roots[2];
2863 SkScalar A = pts[2].fY;
2864 SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
2865 SkScalar C = pts[0].fY;
2866 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
2867 B -= C; // B = b*w - w * yCept + yCept - a
2868 C -= y;
2869 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
2870 SkASSERT(n <= 1);
2871 SkScalar xt;
2872 if (0 == n) {
2873 // zero roots are returned only when y0 == y
2874 // Need [0] if dir == 1
2875 // and [2] if dir == -1
2876 xt = pts[1 - dir].fX;
2877 } else {
2878 SkScalar t = roots[0];
2879 xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
2880 }
2881 if (SkScalarNearlyEqual(xt, x)) {
2882 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
2883 *onCurveCount += 1;
2884 return 0;
2885 }
2886 }
2887 return xt < x ? dir : 0;
2888 }
2889
is_mono_quad(SkScalar y0,SkScalar y1,SkScalar y2)2890 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
2891 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
2892 if (y0 == y1) {
2893 return true;
2894 }
2895 if (y0 < y1) {
2896 return y1 <= y2;
2897 } else {
2898 return y1 >= y2;
2899 }
2900 }
2901
winding_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar weight,int * onCurveCount)2902 static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
2903 int* onCurveCount) {
2904 SkConic conic(pts, weight);
2905 SkConic chopped[2];
2906 // If the data points are very large, the conic may not be monotonic but may also
2907 // fail to chop. Then, the chopper does not split the original conic in two.
2908 bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
2909 int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
2910 if (!isMono) {
2911 w += winding_mono_conic(chopped[1], x, y, onCurveCount);
2912 }
2913 return w;
2914 }
2915
winding_mono_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2916 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2917 SkScalar y0 = pts[0].fY;
2918 SkScalar y2 = pts[2].fY;
2919
2920 int dir = 1;
2921 if (y0 > y2) {
2922 SkTSwap(y0, y2);
2923 dir = -1;
2924 }
2925 if (y < y0 || y > y2) {
2926 return 0;
2927 }
2928 if (checkOnCurve(x, y, pts[0], pts[2])) {
2929 *onCurveCount += 1;
2930 return 0;
2931 }
2932 if (y == y2) {
2933 return 0;
2934 }
2935 // bounds check on X (not required. is it faster?)
2936 #if 0
2937 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
2938 return 0;
2939 }
2940 #endif
2941
2942 SkScalar roots[2];
2943 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2944 2 * (pts[1].fY - pts[0].fY),
2945 pts[0].fY - y,
2946 roots);
2947 SkASSERT(n <= 1);
2948 SkScalar xt;
2949 if (0 == n) {
2950 // zero roots are returned only when y0 == y
2951 // Need [0] if dir == 1
2952 // and [2] if dir == -1
2953 xt = pts[1 - dir].fX;
2954 } else {
2955 SkScalar t = roots[0];
2956 SkScalar C = pts[0].fX;
2957 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2958 SkScalar B = 2 * (pts[1].fX - C);
2959 xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
2960 }
2961 if (SkScalarNearlyEqual(xt, x)) {
2962 if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points
2963 *onCurveCount += 1;
2964 return 0;
2965 }
2966 }
2967 return xt < x ? dir : 0;
2968 }
2969
winding_quad(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2970 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2971 SkPoint dst[5];
2972 int n = 0;
2973
2974 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
2975 n = SkChopQuadAtYExtrema(pts, dst);
2976 pts = dst;
2977 }
2978 int w = winding_mono_quad(pts, x, y, onCurveCount);
2979 if (n > 0) {
2980 w += winding_mono_quad(&pts[2], x, y, onCurveCount);
2981 }
2982 return w;
2983 }
2984
winding_line(const SkPoint pts[],SkScalar x,SkScalar y,int * onCurveCount)2985 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2986 SkScalar x0 = pts[0].fX;
2987 SkScalar y0 = pts[0].fY;
2988 SkScalar x1 = pts[1].fX;
2989 SkScalar y1 = pts[1].fY;
2990
2991 SkScalar dy = y1 - y0;
2992
2993 int dir = 1;
2994 if (y0 > y1) {
2995 SkTSwap(y0, y1);
2996 dir = -1;
2997 }
2998 if (y < y0 || y > y1) {
2999 return 0;
3000 }
3001 if (checkOnCurve(x, y, pts[0], pts[1])) {
3002 *onCurveCount += 1;
3003 return 0;
3004 }
3005 if (y == y1) {
3006 return 0;
3007 }
3008 SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - SkScalarMul(dy, x - x0);
3009
3010 if (!cross) {
3011 // zero cross means the point is on the line, and since the case where
3012 // y of the query point is at the end point is handled above, we can be
3013 // sure that we're on the line (excluding the end point) here
3014 if (x != x1 || y != pts[1].fY) {
3015 *onCurveCount += 1;
3016 }
3017 dir = 0;
3018 } else if (SkScalarSignAsInt(cross) == dir) {
3019 dir = 0;
3020 }
3021 return dir;
3022 }
3023
tangent_cubic(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3024 static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
3025 SkTDArray<SkVector>* tangents) {
3026 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
3027 && !between(pts[2].fY, y, pts[3].fY)) {
3028 return;
3029 }
3030 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
3031 && !between(pts[2].fX, x, pts[3].fX)) {
3032 return;
3033 }
3034 SkPoint dst[10];
3035 int n = SkChopCubicAtYExtrema(pts, dst);
3036 for (int i = 0; i <= n; ++i) {
3037 SkPoint* c = &dst[i * 3];
3038 SkScalar t;
3039 SkAssertResult(SkCubicClipper::ChopMonoAtY(c, y, &t));
3040 SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
3041 if (!SkScalarNearlyEqual(x, xt)) {
3042 continue;
3043 }
3044 SkVector tangent;
3045 SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
3046 tangents->push(tangent);
3047 }
3048 }
3049
tangent_conic(const SkPoint pts[],SkScalar x,SkScalar y,SkScalar w,SkTDArray<SkVector> * tangents)3050 static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
3051 SkTDArray<SkVector>* tangents) {
3052 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3053 return;
3054 }
3055 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3056 return;
3057 }
3058 SkScalar roots[2];
3059 SkScalar A = pts[2].fY;
3060 SkScalar B = pts[1].fY * w - y * w + y;
3061 SkScalar C = pts[0].fY;
3062 A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept)
3063 B -= C; // B = b*w - w * yCept + yCept - a
3064 C -= y;
3065 int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
3066 for (int index = 0; index < n; ++index) {
3067 SkScalar t = roots[index];
3068 SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
3069 if (!SkScalarNearlyEqual(x, xt)) {
3070 continue;
3071 }
3072 SkConic conic(pts, w);
3073 tangents->push(conic.evalTangentAt(t));
3074 }
3075 }
3076
tangent_quad(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3077 static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
3078 SkTDArray<SkVector>* tangents) {
3079 if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
3080 return;
3081 }
3082 if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
3083 return;
3084 }
3085 SkScalar roots[2];
3086 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
3087 2 * (pts[1].fY - pts[0].fY),
3088 pts[0].fY - y,
3089 roots);
3090 for (int index = 0; index < n; ++index) {
3091 SkScalar t = roots[index];
3092 SkScalar C = pts[0].fX;
3093 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
3094 SkScalar B = 2 * (pts[1].fX - C);
3095 SkScalar xt = (A * t + B) * t + C;
3096 if (!SkScalarNearlyEqual(x, xt)) {
3097 continue;
3098 }
3099 tangents->push(SkEvalQuadTangentAt(pts, t));
3100 }
3101 }
3102
tangent_line(const SkPoint pts[],SkScalar x,SkScalar y,SkTDArray<SkVector> * tangents)3103 static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
3104 SkTDArray<SkVector>* tangents) {
3105 SkScalar y0 = pts[0].fY;
3106 SkScalar y1 = pts[1].fY;
3107 if (!between(y0, y, y1)) {
3108 return;
3109 }
3110 SkScalar x0 = pts[0].fX;
3111 SkScalar x1 = pts[1].fX;
3112 if (!between(x0, x, x1)) {
3113 return;
3114 }
3115 SkScalar dx = x1 - x0;
3116 SkScalar dy = y1 - y0;
3117 if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
3118 return;
3119 }
3120 SkVector v;
3121 v.set(dx, dy);
3122 tangents->push(v);
3123 }
3124
contains_inclusive(const SkRect & r,SkScalar x,SkScalar y)3125 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3126 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3127 }
3128
contains(SkScalar x,SkScalar y) const3129 bool SkPath::contains(SkScalar x, SkScalar y) const {
3130 bool isInverse = this->isInverseFillType();
3131 if (this->isEmpty()) {
3132 return isInverse;
3133 }
3134
3135 if (!contains_inclusive(this->getBounds(), x, y)) {
3136 return isInverse;
3137 }
3138
3139 SkPath::Iter iter(*this, true);
3140 bool done = false;
3141 int w = 0;
3142 int onCurveCount = 0;
3143 do {
3144 SkPoint pts[4];
3145 switch (iter.next(pts, false)) {
3146 case SkPath::kMove_Verb:
3147 case SkPath::kClose_Verb:
3148 break;
3149 case SkPath::kLine_Verb:
3150 w += winding_line(pts, x, y, &onCurveCount);
3151 break;
3152 case SkPath::kQuad_Verb:
3153 w += winding_quad(pts, x, y, &onCurveCount);
3154 break;
3155 case SkPath::kConic_Verb:
3156 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3157 break;
3158 case SkPath::kCubic_Verb:
3159 w += winding_cubic(pts, x, y, &onCurveCount);
3160 break;
3161 case SkPath::kDone_Verb:
3162 done = true;
3163 break;
3164 }
3165 } while (!done);
3166 bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
3167 || SkPath::kInverseEvenOdd_FillType == this->getFillType();
3168 if (evenOddFill) {
3169 w &= 1;
3170 }
3171 if (w) {
3172 return !isInverse;
3173 }
3174 if (onCurveCount <= 1) {
3175 return SkToBool(onCurveCount) ^ isInverse;
3176 }
3177 if ((onCurveCount & 1) || evenOddFill) {
3178 return SkToBool(onCurveCount & 1) ^ isInverse;
3179 }
3180 // If the point touches an even number of curves, and the fill is winding, check for
3181 // coincidence. Count coincidence as places where the on curve points have identical tangents.
3182 iter.setPath(*this, true);
3183 done = false;
3184 SkTDArray<SkVector> tangents;
3185 do {
3186 SkPoint pts[4];
3187 int oldCount = tangents.count();
3188 switch (iter.next(pts, false)) {
3189 case SkPath::kMove_Verb:
3190 case SkPath::kClose_Verb:
3191 break;
3192 case SkPath::kLine_Verb:
3193 tangent_line(pts, x, y, &tangents);
3194 break;
3195 case SkPath::kQuad_Verb:
3196 tangent_quad(pts, x, y, &tangents);
3197 break;
3198 case SkPath::kConic_Verb:
3199 tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
3200 break;
3201 case SkPath::kCubic_Verb:
3202 tangent_cubic(pts, x, y, &tangents);
3203 break;
3204 case SkPath::kDone_Verb:
3205 done = true;
3206 break;
3207 }
3208 if (tangents.count() > oldCount) {
3209 int last = tangents.count() - 1;
3210 const SkVector& tangent = tangents[last];
3211 if (SkScalarNearlyZero(tangent.lengthSqd())) {
3212 tangents.remove(last);
3213 } else {
3214 for (int index = 0; index < last; ++index) {
3215 const SkVector& test = tangents[index];
3216 if (SkScalarNearlyZero(test.cross(tangent))
3217 && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
3218 && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
3219 tangents.remove(last);
3220 tangents.removeShuffle(index);
3221 break;
3222 }
3223 }
3224 }
3225 }
3226 } while (!done);
3227 return SkToBool(tangents.count()) ^ isInverse;
3228 }
3229
ConvertConicToQuads(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,SkScalar w,SkPoint pts[],int pow2)3230 int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3231 SkScalar w, SkPoint pts[], int pow2) {
3232 const SkConic conic(p0, p1, p2, w);
3233 return conic.chopIntoQuadsPOW2(pts, pow2);
3234 }
3235