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