• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPathBuilder.h"
9 #include "include/core/SkRRect.h"
10 #include "include/private/SkPathRef.h"
11 #include "include/private/SkSafe32.h"
12 #include "src/core/SkGeometry.h"
13 #include "src/core/SkPathPriv.h"
14 // need SkDVector
15 #include "src/pathops/SkPathOpsPoint.h"
16 
SkPathBuilder()17 SkPathBuilder::SkPathBuilder() {
18     this->reset();
19 }
20 
SkPathBuilder(SkPathFillType ft)21 SkPathBuilder::SkPathBuilder(SkPathFillType ft) {
22     this->reset();
23     fFillType = ft;
24 }
25 
SkPathBuilder(const SkPath & src)26 SkPathBuilder::SkPathBuilder(const SkPath& src) {
27     *this = src;
28 }
29 
~SkPathBuilder()30 SkPathBuilder::~SkPathBuilder() {
31 }
32 
reset()33 SkPathBuilder& SkPathBuilder::reset() {
34     fPts.reset();
35     fVerbs.reset();
36     fConicWeights.reset();
37     fFillType = SkPathFillType::kWinding;
38     fIsVolatile = false;
39 
40     // these are internal state
41 
42     fSegmentMask = 0;
43     fLastMovePoint = {0, 0};
44     fNeedsMoveVerb = true;
45 
46     // testing
47     fOverrideConvexity = SkPathConvexity::kUnknown;
48 
49     return *this;
50 }
51 
operator =(const SkPath & src)52 SkPathBuilder& SkPathBuilder::operator=(const SkPath& src) {
53     this->reset().setFillType(src.getFillType());
54 
55     for (auto [verb, pts, w] : SkPathPriv::Iterate(src)) {
56         switch (verb) {
57             case SkPathVerb::kMove:  this->moveTo(pts[0]); break;
58             case SkPathVerb::kLine:  this->lineTo(pts[1]); break;
59             case SkPathVerb::kQuad:  this->quadTo(pts[1], pts[2]); break;
60             case SkPathVerb::kConic: this->conicTo(pts[1], pts[2], w[0]); break;
61             case SkPathVerb::kCubic: this->cubicTo(pts[1], pts[2], pts[3]); break;
62             case SkPathVerb::kClose: this->close(); break;
63         }
64     }
65     return *this;
66 }
67 
incReserve(int extraPtCount,int extraVbCount)68 void SkPathBuilder::incReserve(int extraPtCount, int extraVbCount) {
69     fPts.setReserve(  Sk32_sat_add(fPts.count(),   extraPtCount));
70     fVerbs.setReserve(Sk32_sat_add(fVerbs.count(), extraVbCount));
71 }
72 
computeBounds() const73 SkRect SkPathBuilder::computeBounds() const {
74     SkRect bounds;
75     bounds.setBounds(fPts.begin(), fPts.count());
76     return bounds;
77 }
78 
79 /*
80  *  Some old behavior in SkPath -- should we keep it?
81  *
82  *  After each edit (i.e. adding a verb)
83         this->setConvexityType(SkPathConvexity::kUnknown);
84         this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
85  */
86 
moveTo(SkPoint pt)87 SkPathBuilder& SkPathBuilder::moveTo(SkPoint pt) {
88     fPts.push_back(pt);
89     fVerbs.push_back((uint8_t)SkPathVerb::kMove);
90 
91     fLastMovePoint = pt;
92     fNeedsMoveVerb = false;
93     return *this;
94 }
95 
lineTo(SkPoint pt)96 SkPathBuilder& SkPathBuilder::lineTo(SkPoint pt) {
97     this->ensureMove();
98 
99     fPts.push_back(pt);
100     fVerbs.push_back((uint8_t)SkPathVerb::kLine);
101 
102     fSegmentMask |= kLine_SkPathSegmentMask;
103     return *this;
104 }
105 
quadTo(SkPoint pt1,SkPoint pt2)106 SkPathBuilder& SkPathBuilder::quadTo(SkPoint pt1, SkPoint pt2) {
107     this->ensureMove();
108 
109     SkPoint* p = fPts.append(2);
110     p[0] = pt1;
111     p[1] = pt2;
112     fVerbs.push_back((uint8_t)SkPathVerb::kQuad);
113 
114     fSegmentMask |= kQuad_SkPathSegmentMask;
115     return *this;
116 }
117 
conicTo(SkPoint pt1,SkPoint pt2,SkScalar w)118 SkPathBuilder& SkPathBuilder::conicTo(SkPoint pt1, SkPoint pt2, SkScalar w) {
119     this->ensureMove();
120 
121     SkPoint* p = fPts.append(2);
122     p[0] = pt1;
123     p[1] = pt2;
124     fVerbs.push_back((uint8_t)SkPathVerb::kConic);
125     fConicWeights.push_back(w);
126 
127     fSegmentMask |= kConic_SkPathSegmentMask;
128     return *this;
129 }
130 
cubicTo(SkPoint pt1,SkPoint pt2,SkPoint pt3)131 SkPathBuilder& SkPathBuilder::cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3) {
132     this->ensureMove();
133 
134     SkPoint* p = fPts.append(3);
135     p[0] = pt1;
136     p[1] = pt2;
137     p[2] = pt3;
138     fVerbs.push_back((uint8_t)SkPathVerb::kCubic);
139 
140     fSegmentMask |= kCubic_SkPathSegmentMask;
141     return *this;
142 }
143 
close()144 SkPathBuilder& SkPathBuilder::close() {
145     if (fVerbs.count() > 0) {
146         this->ensureMove();
147 
148         fVerbs.push_back((uint8_t)SkPathVerb::kClose);
149 
150         // fLastMovePoint stays where it is -- the previous moveTo
151         fNeedsMoveVerb = true;
152     }
153     return *this;
154 }
155 
156 ///////////////////////////////////////////////////////////////////////////////////////////
157 
rLineTo(SkPoint p1)158 SkPathBuilder& SkPathBuilder::rLineTo(SkPoint p1) {
159     this->ensureMove();
160     return this->lineTo(fPts.back() + p1);
161 }
162 
rQuadTo(SkPoint p1,SkPoint p2)163 SkPathBuilder& SkPathBuilder::rQuadTo(SkPoint p1, SkPoint p2) {
164     this->ensureMove();
165     SkPoint base = fPts.back();
166     return this->quadTo(base + p1, base + p2);
167 }
168 
rConicTo(SkPoint p1,SkPoint p2,SkScalar w)169 SkPathBuilder& SkPathBuilder::rConicTo(SkPoint p1, SkPoint p2, SkScalar w) {
170     this->ensureMove();
171     SkPoint base = fPts.back();
172     return this->conicTo(base + p1, base + p2, w);
173 }
174 
rCubicTo(SkPoint p1,SkPoint p2,SkPoint p3)175 SkPathBuilder& SkPathBuilder::rCubicTo(SkPoint p1, SkPoint p2, SkPoint p3) {
176     this->ensureMove();
177     SkPoint base = fPts.back();
178     return this->cubicTo(base + p1, base + p2, base + p3);
179 }
180 
181 ///////////////////////////////////////////////////////////////////////////////////////////
182 
make(sk_sp<SkPathRef> pr) const183 SkPath SkPathBuilder::make(sk_sp<SkPathRef> pr) const {
184     auto convexity = SkPathConvexity::kUnknown;
185     SkPathFirstDirection dir = SkPathFirstDirection::kUnknown;
186 
187     switch (fIsA) {
188         case kIsA_Oval:
189             pr->setIsOval( true, fIsACCW, fIsAStart);
190             convexity = SkPathConvexity::kConvex;
191             dir = fIsACCW ? SkPathFirstDirection::kCCW : SkPathFirstDirection::kCW;
192             break;
193         case kIsA_RRect:
194             pr->setIsRRect(true, fIsACCW, fIsAStart);
195             convexity = SkPathConvexity::kConvex;
196             dir = fIsACCW ? SkPathFirstDirection::kCCW : SkPathFirstDirection::kCW;
197             break;
198         default: break;
199     }
200 
201     if (fOverrideConvexity != SkPathConvexity::kUnknown) {
202         convexity = fOverrideConvexity;
203     }
204 
205     // Wonder if we can combine convexity and dir internally...
206     //  unknown, convex_cw, convex_ccw, concave
207     // Do we ever have direction w/o convexity, or viceversa (inside path)?
208     //
209     return SkPath(std::move(pr), fFillType, fIsVolatile, convexity, dir);
210 }
211 
snapshot() const212 SkPath SkPathBuilder::snapshot() const {
213     return this->make(sk_sp<SkPathRef>(new SkPathRef(fPts,
214                                                      fVerbs,
215                                                      fConicWeights,
216                                                      fSegmentMask)));
217 }
218 
detach()219 SkPath SkPathBuilder::detach() {
220     auto path = this->make(sk_sp<SkPathRef>(new SkPathRef(std::move(fPts),
221                                                           std::move(fVerbs),
222                                                           std::move(fConicWeights),
223                                                           fSegmentMask)));
224     this->reset();
225     return path;
226 }
227 
228 ///////////////////////////////////////////////////////////////////////////////////////////////////
229 
arc_is_lone_point(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,SkPoint * pt)230 static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
231                               SkPoint* pt) {
232     if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
233         // Chrome uses this path to move into and out of ovals. If not
234         // treated as a special case the moves can distort the oval's
235         // bounding box (and break the circle special case).
236         pt->set(oval.fRight, oval.centerY());
237         return true;
238     } else if (0 == oval.width() && 0 == oval.height()) {
239         // Chrome will sometimes create 0 radius round rects. Having degenerate
240         // quad segments in the path prevents the path from being recognized as
241         // a rect.
242         // TODO: optimizing the case where only one of width or height is zero
243         // should also be considered. This case, however, doesn't seem to be
244         // as common as the single point case.
245         pt->set(oval.fRight, oval.fTop);
246         return true;
247     }
248     return false;
249 }
250 
251 // Return the unit vectors pointing at the start/stop points for the given start/sweep angles
252 //
angles_to_unit_vectors(SkScalar startAngle,SkScalar sweepAngle,SkVector * startV,SkVector * stopV,SkRotationDirection * dir)253 static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
254                                    SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
255     SkScalar startRad = SkDegreesToRadians(startAngle),
256              stopRad  = SkDegreesToRadians(startAngle + sweepAngle);
257 
258     startV->fY = SkScalarSinSnapToZero(startRad);
259     startV->fX = SkScalarCosSnapToZero(startRad);
260     stopV->fY = SkScalarSinSnapToZero(stopRad);
261     stopV->fX = SkScalarCosSnapToZero(stopRad);
262 
263     /*  If the sweep angle is nearly (but less than) 360, then due to precision
264      loss in radians-conversion and/or sin/cos, we may end up with coincident
265      vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
266      of drawing a nearly complete circle (good).
267      e.g. canvas.drawArc(0, 359.99, ...)
268      -vs- canvas.drawArc(0, 359.9, ...)
269      We try to detect this edge case, and tweak the stop vector
270      */
271     if (*startV == *stopV) {
272         SkScalar sw = SkScalarAbs(sweepAngle);
273         if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
274             // make a guess at a tiny angle (in radians) to tweak by
275             SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
276             // not sure how much will be enough, so we use a loop
277             do {
278                 stopRad -= deltaRad;
279                 stopV->fY = SkScalarSinSnapToZero(stopRad);
280                 stopV->fX = SkScalarCosSnapToZero(stopRad);
281             } while (*startV == *stopV);
282         }
283     }
284     *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
285 }
286 
287 /**
288  *  If this returns 0, then the caller should just line-to the singlePt, else it should
289  *  ignore singlePt and append the specified number of conics.
290  */
build_arc_conics(const SkRect & oval,const SkVector & start,const SkVector & stop,SkRotationDirection dir,SkConic conics[SkConic::kMaxConicsForArc],SkPoint * singlePt)291 static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
292                             SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
293                             SkPoint* singlePt) {
294     SkMatrix    matrix;
295 
296     matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
297     matrix.postTranslate(oval.centerX(), oval.centerY());
298 
299     int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
300     if (0 == count) {
301         matrix.mapXY(stop.x(), stop.y(), singlePt);
302     }
303     return count;
304 }
305 
nearly_equal(const SkPoint & a,const SkPoint & b)306 static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
307     return SkScalarNearlyEqual(a.fX, b.fX)
308         && SkScalarNearlyEqual(a.fY, b.fY);
309 }
310 
arcTo(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool forceMoveTo)311 SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
312                                     bool forceMoveTo) {
313     if (oval.width() < 0 || oval.height() < 0) {
314         return *this;
315     }
316 
317     if (fVerbs.count() == 0) {
318         forceMoveTo = true;
319     }
320 
321     SkPoint lonePt;
322     if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
323         return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
324     }
325 
326     SkVector startV, stopV;
327     SkRotationDirection dir;
328     angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
329 
330     SkPoint singlePt;
331 
332     // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
333     // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
334     // arcs from the same oval.
335     auto addPt = [forceMoveTo, this](const SkPoint& pt) {
336         if (forceMoveTo) {
337             this->moveTo(pt);
338         } else if (!nearly_equal(fPts.back(), pt)) {
339             this->lineTo(pt);
340         }
341     };
342 
343     // At this point, we know that the arc is not a lone point, but startV == stopV
344     // indicates that the sweepAngle is too small such that angles_to_unit_vectors
345     // cannot handle it.
346     if (startV == stopV) {
347         SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
348         SkScalar radiusX = oval.width() / 2;
349         SkScalar radiusY = oval.height() / 2;
350         // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle
351         // is very small and radius is huge, the expected behavior here is to draw a line. But
352         // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot.
353         singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle),
354                      oval.centerY() + radiusY * SkScalarSin(endAngle));
355         addPt(singlePt);
356         return *this;
357     }
358 
359     SkConic conics[SkConic::kMaxConicsForArc];
360     int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
361     if (count) {
362         this->incReserve(count * 2 + 1);
363         const SkPoint& pt = conics[0].fPts[0];
364         addPt(pt);
365         for (int i = 0; i < count; ++i) {
366             this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
367         }
368     } else {
369         addPt(singlePt);
370     }
371     return *this;
372 }
373 
addArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle)374 SkPathBuilder& SkPathBuilder::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
375     if (oval.isEmpty() || 0 == sweepAngle) {
376         return *this;
377     }
378 
379     const SkScalar kFullCircleAngle = SkIntToScalar(360);
380 
381     if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
382         // We can treat the arc as an oval if it begins at one of our legal starting positions.
383         // See SkPath::addOval() docs.
384         SkScalar startOver90 = startAngle / 90.f;
385         SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
386         SkScalar error = startOver90 - startOver90I;
387         if (SkScalarNearlyEqual(error, 0)) {
388             // Index 1 is at startAngle == 0.
389             SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
390             startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
391             return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW,
392                                  (unsigned) startIndex);
393         }
394     }
395     return this->arcTo(oval, startAngle, sweepAngle, true);
396 }
397 
arcTo(SkPoint p1,SkPoint p2,SkScalar radius)398 SkPathBuilder& SkPathBuilder::arcTo(SkPoint p1, SkPoint p2, SkScalar radius) {
399     this->ensureMove();
400 
401     if (radius == 0) {
402         return this->lineTo(p1);
403     }
404 
405     // need to know our prev pt so we can construct tangent vectors
406     SkPoint start = fPts.back();
407 
408     // need double precision for these calcs.
409     SkDVector befored, afterd;
410     befored.set({p1.fX - start.fX, p1.fY - start.fY}).normalize();
411     afterd.set({p2.fX - p1.fX, p2.fY - p1.fY}).normalize();
412     double cosh = befored.dot(afterd);
413     double sinh = befored.cross(afterd);
414 
415     if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) {
416         return this->lineTo(p1);
417     }
418 
419     // safe to convert back to floats now
420     SkVector before = befored.asSkVector();
421     SkVector after = afterd.asSkVector();
422     SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh));
423     SkScalar xx = p1.fX - dist * before.fX;
424     SkScalar yy = p1.fY - dist * before.fY;
425     after.setLength(dist);
426     this->lineTo(xx, yy);
427     SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5));
428     return this->conicTo(p1, p1 + after, weight);
429 }
430 
431 // This converts the SVG arc to conics.
432 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
433 // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
434 // See also SVG implementation notes:
435 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
436 // Note that arcSweep bool value is flipped from the original implementation.
arcTo(SkPoint rad,SkScalar angle,SkPathBuilder::ArcSize arcLarge,SkPathDirection arcSweep,SkPoint endPt)437 SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder::ArcSize arcLarge,
438                                     SkPathDirection arcSweep, SkPoint endPt) {
439     this->ensureMove();
440 
441     SkPoint srcPts[2] = { fPts.back(), endPt };
442 
443     // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
444     // joining the endpoints.
445     // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
446     if (!rad.fX || !rad.fY) {
447         return this->lineTo(endPt);
448     }
449     // If the current point and target point for the arc are identical, it should be treated as a
450     // zero length path. This ensures continuity in animations.
451     if (srcPts[0] == srcPts[1]) {
452         return this->lineTo(endPt);
453     }
454     SkScalar rx = SkScalarAbs(rad.fX);
455     SkScalar ry = SkScalarAbs(rad.fY);
456     SkVector midPointDistance = srcPts[0] - srcPts[1];
457     midPointDistance *= 0.5f;
458 
459     SkMatrix pointTransform;
460     pointTransform.setRotate(-angle);
461 
462     SkPoint transformedMidPoint;
463     pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
464     SkScalar squareRx = rx * rx;
465     SkScalar squareRy = ry * ry;
466     SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
467     SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
468 
469     // Check if the radii are big enough to draw the arc, scale radii if not.
470     // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
471     SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
472     if (radiiScale > 1) {
473         radiiScale = SkScalarSqrt(radiiScale);
474         rx *= radiiScale;
475         ry *= radiiScale;
476     }
477 
478     pointTransform.setScale(1 / rx, 1 / ry);
479     pointTransform.preRotate(-angle);
480 
481     SkPoint unitPts[2];
482     pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
483     SkVector delta = unitPts[1] - unitPts[0];
484 
485     SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
486     SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f);
487 
488     SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
489     if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) {  // flipped from the original implementation
490         scaleFactor = -scaleFactor;
491     }
492     delta.scale(scaleFactor);
493     SkPoint centerPoint = unitPts[0] + unitPts[1];
494     centerPoint *= 0.5f;
495     centerPoint.offset(-delta.fY, delta.fX);
496     unitPts[0] -= centerPoint;
497     unitPts[1] -= centerPoint;
498     SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
499     SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
500     SkScalar thetaArc = theta2 - theta1;
501     if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
502         thetaArc += SK_ScalarPI * 2;
503     } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
504         thetaArc -= SK_ScalarPI * 2;
505     }
506 
507     // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272)
508     // so we do a quick check here. The precise tolerance amount is just made up.
509     // PI/million happens to fix the bug in 9272, but a larger value is probably
510     // ok too.
511     if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) {
512         return this->lineTo(endPt);
513     }
514 
515     pointTransform.setRotate(angle);
516     pointTransform.preScale(rx, ry);
517 
518     // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd
519     int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3)));
520     SkScalar thetaWidth = thetaArc / segments;
521     SkScalar t = SkScalarTan(0.5f * thetaWidth);
522     if (!SkScalarIsFinite(t)) {
523         return *this;
524     }
525     SkScalar startTheta = theta1;
526     SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
527     auto scalar_is_integer = [](SkScalar scalar) -> bool {
528         return scalar == SkScalarFloorToScalar(scalar);
529     };
530     bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) &&
531         scalar_is_integer(rx) && scalar_is_integer(ry) &&
532         scalar_is_integer(endPt.fX) && scalar_is_integer(endPt.fY);
533 
534     for (int i = 0; i < segments; ++i) {
535         SkScalar endTheta    = startTheta + thetaWidth,
536                  sinEndTheta = SkScalarSinSnapToZero(endTheta),
537                  cosEndTheta = SkScalarCosSnapToZero(endTheta);
538 
539         unitPts[1].set(cosEndTheta, sinEndTheta);
540         unitPts[1] += centerPoint;
541         unitPts[0] = unitPts[1];
542         unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
543         SkPoint mapped[2];
544         pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
545         /*
546         Computing the arc width introduces rounding errors that cause arcs to start
547         outside their marks. A round rect may lose convexity as a result. If the input
548         values are on integers, place the conic on integers as well.
549          */
550         if (expectIntegers) {
551             for (SkPoint& point : mapped) {
552                 point.fX = SkScalarRoundToScalar(point.fX);
553                 point.fY = SkScalarRoundToScalar(point.fY);
554             }
555         }
556         this->conicTo(mapped[0], mapped[1], w);
557         startTheta = endTheta;
558     }
559 
560 #ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT
561     // The final point should match the input point (by definition); replace it to
562     // ensure that rounding errors in the above math don't cause any problems.
563     fPts.back() = endPt;
564 #endif
565     return *this;
566 }
567 
568 ///////////////////////////////////////////////////////////////////////////////////////////
569 
570 namespace {
571     template <unsigned N> class PointIterator {
572     public:
PointIterator(SkPathDirection dir,unsigned startIndex)573         PointIterator(SkPathDirection dir, unsigned startIndex)
574             : fCurrent(startIndex % N)
575             , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1)
576         {}
577 
current() const578         const SkPoint& current() const {
579             SkASSERT(fCurrent < N);
580             return fPts[fCurrent];
581         }
582 
next()583         const SkPoint& next() {
584             fCurrent = (fCurrent + fAdvance) % N;
585             return this->current();
586         }
587 
588     protected:
589         SkPoint fPts[N];
590 
591     private:
592         unsigned fCurrent;
593         unsigned fAdvance;
594     };
595 
596     class RectPointIterator : public PointIterator<4> {
597     public:
RectPointIterator(const SkRect & rect,SkPathDirection dir,unsigned startIndex)598         RectPointIterator(const SkRect& rect, SkPathDirection dir, unsigned startIndex)
599         : PointIterator(dir, startIndex) {
600 
601             fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
602             fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
603             fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
604             fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
605         }
606     };
607 
608     class OvalPointIterator : public PointIterator<4> {
609     public:
OvalPointIterator(const SkRect & oval,SkPathDirection dir,unsigned startIndex)610         OvalPointIterator(const SkRect& oval, SkPathDirection dir, unsigned startIndex)
611         : PointIterator(dir, startIndex) {
612 
613             const SkScalar cx = oval.centerX();
614             const SkScalar cy = oval.centerY();
615 
616             fPts[0] = SkPoint::Make(cx, oval.fTop);
617             fPts[1] = SkPoint::Make(oval.fRight, cy);
618             fPts[2] = SkPoint::Make(cx, oval.fBottom);
619             fPts[3] = SkPoint::Make(oval.fLeft, cy);
620         }
621     };
622 
623     class RRectPointIterator : public PointIterator<8> {
624     public:
RRectPointIterator(const SkRRect & rrect,SkPathDirection dir,unsigned startIndex)625         RRectPointIterator(const SkRRect& rrect, SkPathDirection dir, unsigned startIndex)
626             : PointIterator(dir, startIndex)
627         {
628             const SkRect& bounds = rrect.getBounds();
629             const SkScalar L = bounds.fLeft;
630             const SkScalar T = bounds.fTop;
631             const SkScalar R = bounds.fRight;
632             const SkScalar B = bounds.fBottom;
633 
634             fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
635             fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
636             fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
637             fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
638             fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
639             fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
640             fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
641             fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
642         }
643     };
644 } // anonymous namespace
645 
646 
addRect(const SkRect & rect,SkPathDirection dir,unsigned index)647 SkPathBuilder& SkPathBuilder::addRect(const SkRect& rect, SkPathDirection dir, unsigned index) {
648     const int kPts   = 4;   // moveTo + 3 lines
649     const int kVerbs = 5;   // moveTo + 3 lines + close
650     this->incReserve(kPts, kVerbs);
651 
652     RectPointIterator iter(rect, dir, index);
653 
654     this->moveTo(iter.current());
655     this->lineTo(iter.next());
656     this->lineTo(iter.next());
657     this->lineTo(iter.next());
658     return this->close();
659 }
660 
addOval(const SkRect & oval,SkPathDirection dir,unsigned index)661 SkPathBuilder& SkPathBuilder::addOval(const SkRect& oval, SkPathDirection dir, unsigned index) {
662     const IsA prevIsA = fIsA;
663 
664     const int kPts   = 9;   // moveTo + 4 conics(2 pts each)
665     const int kVerbs = 6;   // moveTo + 4 conics + close
666     this->incReserve(kPts, kVerbs);
667 
668     OvalPointIterator ovalIter(oval, dir, index);
669     RectPointIterator rectIter(oval, dir, index + (dir == SkPathDirection::kCW ? 0 : 1));
670 
671     // The corner iterator pts are tracking "behind" the oval/radii pts.
672 
673     this->moveTo(ovalIter.current());
674     for (unsigned i = 0; i < 4; ++i) {
675         this->conicTo(rectIter.next(), ovalIter.next(), SK_ScalarRoot2Over2);
676     }
677     this->close();
678 
679     if (prevIsA == kIsA_JustMoves) {
680         fIsA      = kIsA_Oval;
681         fIsACCW   = (dir == SkPathDirection::kCCW);
682         fIsAStart = index % 4;
683     }
684     return *this;
685 }
686 
addRRect(const SkRRect & rrect,SkPathDirection dir,unsigned index)687 SkPathBuilder& SkPathBuilder::addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned index) {
688     const IsA prevIsA = fIsA;
689     const SkRect& bounds = rrect.getBounds();
690 
691     if (rrect.isRect() || rrect.isEmpty()) {
692         // degenerate(rect) => radii points are collapsing
693         this->addRect(bounds, dir, (index + 1) / 2);
694     } else if (rrect.isOval()) {
695         // degenerate(oval) => line points are collapsing
696         this->addOval(bounds, dir, index / 2);
697     } else {
698         // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
699         const bool startsWithConic = ((index & 1) == (dir == SkPathDirection::kCW));
700         const SkScalar weight = SK_ScalarRoot2Over2;
701 
702         const int kVerbs = startsWithConic
703             ? 9   // moveTo + 4x conicTo + 3x lineTo + close
704             : 10; // moveTo + 4x lineTo + 4x conicTo + close
705         this->incReserve(kVerbs);
706 
707         RRectPointIterator rrectIter(rrect, dir, index);
708         // Corner iterator indices follow the collapsed radii model,
709         // adjusted such that the start pt is "behind" the radii start pt.
710         const unsigned rectStartIndex = index / 2 + (dir == SkPathDirection::kCW ? 0 : 1);
711         RectPointIterator rectIter(bounds, dir, rectStartIndex);
712 
713         this->moveTo(rrectIter.current());
714         if (startsWithConic) {
715             for (unsigned i = 0; i < 3; ++i) {
716                 this->conicTo(rectIter.next(), rrectIter.next(), weight);
717                 this->lineTo(rrectIter.next());
718             }
719             this->conicTo(rectIter.next(), rrectIter.next(), weight);
720             // final lineTo handled by close().
721         } else {
722             for (unsigned i = 0; i < 4; ++i) {
723                 this->lineTo(rrectIter.next());
724                 this->conicTo(rectIter.next(), rrectIter.next(), weight);
725             }
726         }
727         this->close();
728     }
729 
730     if (prevIsA == kIsA_JustMoves) {
731         fIsA      = kIsA_RRect;
732         fIsACCW   = (dir == SkPathDirection::kCCW);
733         fIsAStart = index % 8;
734     }
735     return *this;
736 }
737 
addCircle(SkScalar x,SkScalar y,SkScalar r,SkPathDirection dir)738 SkPathBuilder& SkPathBuilder::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
739     if (r >= 0) {
740         this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
741     }
742     return *this;
743 }
744 
addPolygon(const SkPoint pts[],int count,bool isClosed)745 SkPathBuilder& SkPathBuilder::addPolygon(const SkPoint pts[], int count, bool isClosed) {
746     if (count <= 0) {
747         return *this;
748     }
749 
750     this->moveTo(pts[0]);
751     this->polylineTo(&pts[1], count - 1);
752     if (isClosed) {
753         this->close();
754     }
755     return *this;
756 }
757 
polylineTo(const SkPoint pts[],int count)758 SkPathBuilder& SkPathBuilder::polylineTo(const SkPoint pts[], int count) {
759     if (count > 0) {
760         this->ensureMove();
761 
762         this->incReserve(count, count);
763         memcpy(fPts.append(count), pts, count * sizeof(SkPoint));
764         memset(fVerbs.append(count), (uint8_t)SkPathVerb::kLine, count);
765         fSegmentMask |= kLine_SkPathSegmentMask;
766     }
767     return *this;
768 }
769 
770 //////////////////////////////////////////////////////////////////////////////////////////////////
771 
offset(SkScalar dx,SkScalar dy)772 SkPathBuilder& SkPathBuilder::offset(SkScalar dx, SkScalar dy) {
773     for (auto& p : fPts) {
774         p += {dx, dy};
775     }
776     return *this;
777 }
778 
addPath(const SkPath & src)779 SkPathBuilder& SkPathBuilder::addPath(const SkPath& src) {
780     SkPath::RawIter iter(src);
781     SkPoint pts[4];
782     SkPath::Verb verb;
783 
784     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
785         switch (verb) {
786             case SkPath::kMove_Verb:  this->moveTo (pts[0]); break;
787             case SkPath::kLine_Verb:  this->lineTo (pts[1]); break;
788             case SkPath::kQuad_Verb:  this->quadTo (pts[1], pts[2]); break;
789             case SkPath::kCubic_Verb: this->cubicTo(pts[1], pts[2], pts[3]); break;
790             case SkPath::kConic_Verb: this->conicTo(pts[1], pts[2], iter.conicWeight()); break;
791             case SkPath::kClose_Verb: this->close(); break;
792             case SkPath::kDone_Verb: SkUNREACHABLE;
793         }
794     }
795 
796     return *this;
797 }
798 
privateReverseAddPath(const SkPath & src)799 SkPathBuilder& SkPathBuilder::privateReverseAddPath(const SkPath& src) {
800 
801     const uint8_t* verbsBegin = src.fPathRef->verbsBegin();
802     const uint8_t* verbs = src.fPathRef->verbsEnd();
803     const SkPoint* pts = src.fPathRef->pointsEnd();
804     const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
805 
806     bool needMove = true;
807     bool needClose = false;
808     while (verbs > verbsBegin) {
809         uint8_t v = *--verbs;
810         int n = SkPathPriv::PtsInVerb(v);
811 
812         if (needMove) {
813             --pts;
814             this->moveTo(pts->fX, pts->fY);
815             needMove = false;
816         }
817         pts -= n;
818         switch ((SkPathVerb)v) {
819             case SkPathVerb::kMove:
820                 if (needClose) {
821                     this->close();
822                     needClose = false;
823                 }
824                 needMove = true;
825                 pts += 1;   // so we see the point in "if (needMove)" above
826                 break;
827             case SkPathVerb::kLine:
828                 this->lineTo(pts[0]);
829                 break;
830             case SkPathVerb::kQuad:
831                 this->quadTo(pts[1], pts[0]);
832                 break;
833             case SkPathVerb::kConic:
834                 this->conicTo(pts[1], pts[0], *--conicWeights);
835                 break;
836             case SkPathVerb::kCubic:
837                 this->cubicTo(pts[2], pts[1], pts[0]);
838                 break;
839             case SkPathVerb::kClose:
840                 needClose = true;
841                 break;
842             default:
843                 SkDEBUGFAIL("unexpected verb");
844         }
845     }
846     return *this;
847 }
848