xref: /third_party/skia/src/core/SkPathBuilder.cpp (revision cb93a386)
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
17SkPathBuilder::SkPathBuilder() {
18    this->reset();
19}
20
21SkPathBuilder::SkPathBuilder(SkPathFillType ft) {
22    this->reset();
23    fFillType = ft;
24}
25
26SkPathBuilder::SkPathBuilder(const SkPath& src) {
27    *this = src;
28}
29
30SkPathBuilder::~SkPathBuilder() {
31}
32
33SkPathBuilder& 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
53SkPathBuilder& 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
69void 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
74SkRect 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
88SkPathBuilder& 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
100SkPathBuilder& 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
110SkPathBuilder& 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
122SkPathBuilder& 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
135SkPathBuilder& 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
148SkPathBuilder& 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
162SkPathBuilder& SkPathBuilder::rLineTo(SkPoint p1) {
163    this->ensureMove();
164    return this->lineTo(fPts.back() + p1);
165}
166
167SkPathBuilder& SkPathBuilder::rQuadTo(SkPoint p1, SkPoint p2) {
168    this->ensureMove();
169    SkPoint base = fPts.back();
170    return this->quadTo(base + p1, base + p2);
171}
172
173SkPathBuilder& 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
179SkPathBuilder& 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
187SkPath 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
229SkPath SkPathBuilder::snapshot() const {
230    return this->make(sk_sp<SkPathRef>(new SkPathRef(fPts,
231                                                     fVerbs,
232                                                     fConicWeights,
233                                                     fSegmentMask)));
234}
235
236SkPath 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
247static 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//
270static 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 */
308static 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
323static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
324    return SkScalarNearlyEqual(a.fX, b.fX)
325        && SkScalarNearlyEqual(a.fY, b.fY);
326}
327
328SkPathBuilder& 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
391SkPathBuilder& 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
415SkPathBuilder& 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.
454SkPathBuilder& 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
587namespace {
588    template <unsigned N> class PointIterator {
589    public:
590        PointIterator(SkPathDirection dir, unsigned startIndex)
591            : fCurrent(startIndex % N)
592            , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1)
593        {}
594
595        const SkPoint& current() const {
596            SkASSERT(fCurrent < N);
597            return fPts[fCurrent];
598        }
599
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:
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:
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:
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
664SkPathBuilder& 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
678SkPathBuilder& 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
704SkPathBuilder& 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
755SkPathBuilder& 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
762SkPathBuilder& 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
775SkPathBuilder& 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
789SkPathBuilder& SkPathBuilder::offset(SkScalar dx, SkScalar dy) {
790    for (auto& p : fPts) {
791        p += {dx, dy};
792    }
793    return *this;
794}
795
796SkPathBuilder& 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
816SkPathBuilder& 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