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