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