tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SkStroke.cpp (63041B)


      1 /*
      2 * Copyright 2008 The Android Open Source Project
      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 "src/core/SkStroke.h"
      9 
     10 #include "include/core/SkPath.h"
     11 #include "include/core/SkPathBuilder.h"
     12 #include "include/core/SkPoint.h"
     13 #include "include/core/SkRRect.h"
     14 #include "include/core/SkRect.h"
     15 #include "include/core/SkScalar.h"
     16 #include "include/core/SkSpan.h"
     17 #include "include/private/base/SkFloatingPoint.h"
     18 #include "include/private/base/SkMacros.h"
     19 #include "include/private/base/SkTo.h"
     20 #include "src/core/SkGeometry.h"
     21 #include "src/core/SkPathEnums.h"
     22 #include "src/core/SkPathPriv.h"
     23 #include "src/core/SkPointPriv.h"
     24 #include "src/core/SkStrokerPriv.h"
     25 
     26 #include <algorithm>
     27 #include <array>
     28 #include <optional>
     29 
     30 enum {
     31    kTangent_RecursiveLimit,
     32    kCubic_RecursiveLimit,
     33    kConic_RecursiveLimit,
     34    kQuad_RecursiveLimit
     35 };
     36 
     37 // quads with extreme widths (e.g. (0,1) (1,6) (0,3) width=5e7) recurse to point of failure
     38 // largest seen for normal cubics : 5, 26
     39 // largest seen for normal quads : 11
     40 // 3x limits seen in practice, except for cubics (3x limit would be ~75).
     41 // For cubics, we never get close to 75 when running through dm. The limit of 24
     42 // was chosen because it's close to the peak in a count of cubic recursion depths visited
     43 // (define DEBUG_CUBIC_RECURSION_DEPTHS) and no diffs were produced on gold when using it.
     44 static const int kRecursiveLimits[] = { 5*3, 24, 11*3, 11*3 };
     45 
     46 static_assert(0 == kTangent_RecursiveLimit, "cubic_stroke_relies_on_tangent_equalling_zero");
     47 static_assert(1 == kCubic_RecursiveLimit, "cubic_stroke_relies_on_cubic_equalling_one");
     48 static_assert(std::size(kRecursiveLimits) == kQuad_RecursiveLimit + 1,
     49              "recursive_limits_mismatch");
     50 
     51 #if defined SK_DEBUG && QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
     52    int gMaxRecursion[std::size(kRecursiveLimits)] = { 0 };
     53 #endif
     54 #ifndef DEBUG_QUAD_STROKER
     55    #define DEBUG_QUAD_STROKER 0
     56 #endif
     57 
     58 #if DEBUG_QUAD_STROKER
     59    /* Enable to show the decisions made in subdividing the curve -- helpful when the resulting
     60        stroke has more than the optimal number of quadratics and lines */
     61    #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \
     62            SkDebugf("[%d] %s " format "\n", depth, __FUNCTION__, __VA_ARGS__), \
     63            SkDebugf("  " #resultType " t=(%g,%g)\n", quadPts->fStartT, quadPts->fEndT), \
     64            resultType
     65    #define STROKER_DEBUG_PARAMS(...) , __VA_ARGS__
     66 #else
     67    #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \
     68            resultType
     69    #define STROKER_DEBUG_PARAMS(...)
     70 #endif
     71 
     72 #ifndef DEBUG_CUBIC_RECURSION_DEPTHS
     73 #define DEBUG_CUBIC_RECURSION_DEPTHS 0
     74 #endif
     75 #if DEBUG_CUBIC_RECURSION_DEPTHS
     76    /* Prints a histogram of recursion depths at process termination. */
     77    static struct DepthHistogram {
     78        inline static constexpr int kMaxDepth = 75;
     79        int fCubicDepths[kMaxDepth + 1];
     80 
     81        DepthHistogram() { memset(fCubicDepths, 0, sizeof(fCubicDepths)); }
     82 
     83        ~DepthHistogram() {
     84            SkDebugf("# times recursion terminated per depth:\n");
     85            for (int i = 0; i <= kMaxDepth; i++) {
     86                SkDebugf("  depth %d: %d\n", i, fCubicDepths[i]);
     87            }
     88        }
     89 
     90        inline void incDepth(int depth) {
     91            SkASSERT(depth >= 0 && depth <= kMaxDepth);
     92            fCubicDepths[depth]++;
     93        }
     94    } sCubicDepthHistogram;
     95 
     96 #define DEBUG_CUBIC_RECURSION_TRACK_DEPTH(depth) sCubicDepthHistogram.incDepth(depth)
     97 #else
     98 #define DEBUG_CUBIC_RECURSION_TRACK_DEPTH(depth) (void)(depth)
     99 #endif
    100 
    101 static inline bool degenerate_vector(const SkVector& v) {
    102    return !SkPointPriv::CanNormalize(v.fX, v.fY);
    103 }
    104 
    105 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, SkScalar scale,
    106                                  SkScalar radius,
    107                                  SkVector* normal, SkVector* unitNormal) {
    108    if (!unitNormal->setNormalize((after.fX - before.fX) * scale,
    109                                  (after.fY - before.fY) * scale)) {
    110        return false;
    111    }
    112    SkPointPriv::RotateCCW(unitNormal);
    113    unitNormal->scale(radius, normal);
    114    return true;
    115 }
    116 
    117 static bool set_normal_unitnormal(const SkVector& vec,
    118                                  SkScalar radius,
    119                                  SkVector* normal, SkVector* unitNormal) {
    120    if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
    121        return false;
    122    }
    123    SkPointPriv::RotateCCW(unitNormal);
    124    unitNormal->scale(radius, normal);
    125    return true;
    126 }
    127 
    128 ///////////////////////////////////////////////////////////////////////////////
    129 
    130 struct SkQuadConstruct {    // The state of the quad stroke under construction.
    131    SkPoint fQuad[3];       // the stroked quad parallel to the original curve
    132    SkVector fTangentStart; // tangent vector at fQuad[0]
    133    SkVector fTangentEnd;   // tangent vector at fQuad[2]
    134    SkScalar fStartT;       // a segment of the original curve
    135    SkScalar fMidT;         //              "
    136    SkScalar fEndT;         //              "
    137    bool fStartSet;         // state to share common points across structs
    138    bool fEndSet;           //                     "
    139    bool fOppositeTangents; // set if coincident tangents have opposite directions
    140 
    141    // return false if start and end are too close to have a unique middle
    142    bool init(SkScalar start, SkScalar end) {
    143        fStartT = start;
    144        fMidT = (start + end) * SK_ScalarHalf;
    145        fEndT = end;
    146        fStartSet = fEndSet = false;
    147        return fStartT < fMidT && fMidT < fEndT;
    148    }
    149 
    150    bool initWithStart(SkQuadConstruct* parent) {
    151        if (!init(parent->fStartT, parent->fMidT)) {
    152            return false;
    153        }
    154        fQuad[0] = parent->fQuad[0];
    155        fTangentStart = parent->fTangentStart;
    156        fStartSet = true;
    157        return true;
    158    }
    159 
    160    bool initWithEnd(SkQuadConstruct* parent) {
    161        if (!init(parent->fMidT, parent->fEndT)) {
    162            return false;
    163        }
    164        fQuad[2] = parent->fQuad[2];
    165        fTangentEnd = parent->fTangentEnd;
    166        fEndSet = true;
    167        return true;
    168   }
    169 };
    170 
    171 static bool isZeroLengthSincePoint(SkSpan<const SkPoint> span, int startPtIndex) {
    172    int count = SkToInt(span.size()) - startPtIndex;
    173    if (count < 2) {
    174        return true;
    175    }
    176    const SkPoint* pts = span.data() + startPtIndex;
    177    const SkPoint& first = *pts;
    178    for (int index = 1; index < count; ++index) {
    179        if (first != pts[index]) {
    180            return false;
    181        }
    182    }
    183    return true;
    184 }
    185 
    186 class SkPathStroker {
    187 public:
    188    SkPathStroker(const SkPath& src,
    189                  SkScalar radius, SkScalar miterLimit, SkPaint::Cap,
    190                  SkPaint::Join, SkScalar resScale,
    191                  bool canIgnoreCenter);
    192 
    193    bool hasOnlyMoveTo() const { return 0 == fSegmentCount; }
    194    SkPoint moveToPt() const { return fFirstPt; }
    195 
    196    void moveTo(const SkPoint&);
    197    void lineTo(const SkPoint&, const SkPath::Iter* iter = nullptr);
    198    void quadTo(const SkPoint&, const SkPoint&);
    199    void conicTo(const SkPoint&, const SkPoint&, SkScalar weight);
    200    void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
    201    void close(bool isLine) { this->finishContour(true, isLine); }
    202 
    203    void done(SkPathBuilder* dst, bool isLine) {
    204        this->finishContour(false, isLine);
    205        *dst = fOuter;
    206        fOuter.reset(); // is this needed? we used to "swap" it with dst
    207    }
    208 
    209    SkScalar getResScale() const { return fResScale; }
    210 
    211    bool isCurrentContourEmpty() const {
    212        return isZeroLengthSincePoint(fInner.points(), 0) &&
    213               isZeroLengthSincePoint(fOuter.points(), fFirstOuterPtIndexInContour);
    214    }
    215 
    216 private:
    217    SkScalar    fRadius;
    218    SkScalar    fInvMiterLimit;
    219    SkScalar    fResScale;
    220    SkScalar    fInvResScale;
    221    SkScalar    fInvResScaleSquared;
    222 
    223    SkVector    fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
    224    SkPoint     fFirstPt, fPrevPt;  // on original path
    225    SkPoint     fFirstOuterPt;
    226    int         fFirstOuterPtIndexInContour;
    227    int         fSegmentCount;
    228    bool        fPrevIsLine;
    229    bool        fCanIgnoreCenter;
    230 
    231    SkStrokerPriv::CapProc  fCapper;
    232    SkStrokerPriv::JoinProc fJoiner;
    233 
    234    SkPathBuilder  fInner, fOuter, fCusper; // outer is our working answer, inner is temp
    235 
    236    enum StrokeType {
    237        kOuter_StrokeType = 1,      // use sign-opposite values later to flip perpendicular axis
    238        kInner_StrokeType = -1
    239    } fStrokeType;
    240 
    241    enum ResultType {
    242        kSplit_ResultType,          // the caller should split the quad stroke in two
    243        kDegenerate_ResultType,     // the caller should add a line
    244        kQuad_ResultType,           // the caller should (continue to try to) add a quad stroke
    245    };
    246 
    247    enum ReductionType {
    248        kPoint_ReductionType,       // all curve points are practically identical
    249        kLine_ReductionType,        // the control point is on the line between the ends
    250        kQuad_ReductionType,        // the control point is outside the line between the ends
    251        kDegenerate_ReductionType,  // the control point is on the line but outside the ends
    252        kDegenerate2_ReductionType, // two control points are on the line but outside ends (cubic)
    253        kDegenerate3_ReductionType, // three areas of max curvature found (for cubic)
    254    };
    255 
    256    enum IntersectRayType {
    257        kCtrlPt_RayType,
    258        kResultType_RayType,
    259    };
    260 
    261    int fRecursionDepth;            // track stack depth to abort if numerics run amok
    262    bool fFoundTangents;            // do less work until tangents meet (cubic)
    263    bool fJoinCompleted;            // previous join was not degenerate
    264 
    265    void addDegenerateLine(const SkQuadConstruct* );
    266    static ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction);
    267    static ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3],
    268                                   const SkPoint** tanPtPtr);
    269    static ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction);
    270    ResultType compareQuadConic(const SkConic& , SkQuadConstruct* ) const;
    271    ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* );
    272    ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* );
    273    void conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt,
    274                      SkVector* tangent) const;
    275    void conicQuadEnds(const SkConic& , SkQuadConstruct* ) const;
    276    bool conicStroke(const SkConic& , SkQuadConstruct* );
    277    bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const;
    278    void cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt,
    279                      SkVector* tangent) const;
    280    void cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* );
    281    void cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint* mid) const;
    282    bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* );
    283    void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScalar tEnd);
    284    ResultType intersectRay(SkQuadConstruct* , IntersectRayType  STROKER_DEBUG_PARAMS(int) ) const;
    285    bool ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const;
    286    void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt,
    287                     SkPoint* tangent) const;
    288    bool quadStroke(const SkPoint quad[3], SkQuadConstruct* );
    289    void setConicEndNormal(const SkConic& ,
    290                           const SkVector& normalAB, const SkVector& unitNormalAB,
    291                           SkVector* normalBC, SkVector* unitNormalBC);
    292    void setCubicEndNormal(const SkPoint cubic[4],
    293                           const SkVector& normalAB, const SkVector& unitNormalAB,
    294                           SkVector* normalCD, SkVector* unitNormalCD);
    295    void setQuadEndNormal(const SkPoint quad[3],
    296                          const SkVector& normalAB, const SkVector& unitNormalAB,
    297                          SkVector* normalBC, SkVector* unitNormalBC);
    298    void setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, SkVector* tangent) const;
    299    static bool SlightAngle(SkQuadConstruct* );
    300    ResultType strokeCloseEnough(const SkPoint stroke[3], const SkPoint ray[2],
    301                                 SkQuadConstruct*  STROKER_DEBUG_PARAMS(int depth) ) const;
    302    ResultType tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* );
    303 
    304    void    finishContour(bool close, bool isLine);
    305    bool    preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
    306                      bool isLine);
    307    void    postJoinTo(const SkPoint&, const SkVector& normal,
    308                       const SkVector& unitNormal);
    309 
    310    void    line_to(const SkPoint& currPt, const SkVector& normal);
    311 };
    312 
    313 ///////////////////////////////////////////////////////////////////////////////
    314 
    315 bool SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
    316                              SkVector* unitNormal, bool currIsLine) {
    317    SkASSERT(fSegmentCount >= 0);
    318 
    319    if (!set_normal_unitnormal(fPrevPt, currPt, fResScale, fRadius, normal, unitNormal)) {
    320        if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper) {
    321            return false;
    322        }
    323        /* Square caps and round caps draw even if the segment length is zero.
    324           Since the zero length segment has no direction, set the orientation
    325           to upright as the default orientation */
    326        normal->set(fRadius, 0);
    327        unitNormal->set(1, 0);
    328    }
    329 
    330    if (fSegmentCount == 0) {
    331        fFirstNormal = *normal;
    332        fFirstUnitNormal = *unitNormal;
    333        fFirstOuterPt = fPrevPt + *normal;
    334 
    335        fOuter.moveTo(fFirstOuterPt);
    336        fInner.moveTo(fPrevPt - *normal);
    337    } else {    // we have a previous segment
    338        fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
    339                fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
    340    }
    341    fPrevIsLine = currIsLine;
    342    return true;
    343 }
    344 
    345 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
    346                               const SkVector& unitNormal) {
    347    fJoinCompleted = true;
    348    fPrevPt = currPt;
    349    fPrevUnitNormal = unitNormal;
    350    fPrevNormal = normal;
    351    fSegmentCount += 1;
    352 }
    353 
    354 void SkPathStroker::finishContour(bool close, bool currIsLine) {
    355    if (fSegmentCount > 0) {
    356        if (close) {
    357            fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
    358                    fFirstUnitNormal, fRadius, fInvMiterLimit,
    359                    fPrevIsLine, currIsLine);
    360            fOuter.close();
    361 
    362            if (fCanIgnoreCenter) {
    363                // If we can ignore the center just make sure the larger of the two paths
    364                // is preserved and don't add the smaller one.
    365                if (fInner.computeBounds().contains(fOuter.computeBounds())) {
    366                    fOuter = fInner;
    367                }
    368            } else {
    369                // now add fInner as its own contour
    370                if (auto pt = fInner.getLastPt()) {
    371                    fOuter.moveTo(*pt);
    372                    fOuter.privateReversePathTo(fInner.detach()); // todo: take builder or raw
    373                    fOuter.close();
    374                }
    375            }
    376        } else {    // add caps to start and end
    377            // cap the end
    378            if (auto pt = fInner.getLastPt()) {
    379                fCapper(&fOuter, fPrevPt, fPrevNormal, *pt, currIsLine);
    380                fOuter.privateReversePathTo(fInner.detach());
    381                // cap the start
    382                fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, fPrevIsLine);
    383                fOuter.close();
    384            }
    385        }
    386        if (!fCusper.isEmpty()) {
    387            fOuter.addPath(fCusper.detach());
    388        }
    389    }
    390    fInner.reset();
    391    fSegmentCount = -1;
    392    fFirstOuterPtIndexInContour = fOuter.countPoints();
    393 }
    394 
    395 ///////////////////////////////////////////////////////////////////////////////
    396 
    397 SkPathStroker::SkPathStroker(const SkPath& src,
    398                             SkScalar radius, SkScalar miterLimit,
    399                             SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale,
    400                             bool canIgnoreCenter)
    401        : fRadius(radius)
    402        , fResScale(resScale)
    403        , fCanIgnoreCenter(canIgnoreCenter) {
    404 
    405    /*  This is only used when join is miter_join, but we initialize it here
    406        so that it is always defined, to fix sanitizer warnings.
    407    */
    408    fInvMiterLimit = 0;
    409 
    410    if (join == SkPaint::kMiter_Join) {
    411        if (miterLimit <= SK_Scalar1) {
    412            join = SkPaint::kBevel_Join;
    413        } else {
    414            fInvMiterLimit = SkScalarInvert(miterLimit);
    415        }
    416    }
    417    fCapper = SkStrokerPriv::CapFactory(cap);
    418    fJoiner = SkStrokerPriv::JoinFactory(join);
    419    fSegmentCount = -1;
    420    fFirstOuterPtIndexInContour = 0;
    421    fPrevIsLine = false;
    422 
    423    // Need some estimate of how large our final result (fOuter)
    424    // and our per-contour temp (fInner) will be, so we don't spend
    425    // extra time repeatedly growing these arrays.
    426    //
    427    // 3x for result == inner + outer + join (swag)
    428    // 1x for inner == 'wag' (worst contour length would be better guess)
    429    fOuter.incReserve(src.countPoints() * 3);
    430    fOuter.setIsVolatile(true);
    431    fInner.incReserve(src.countPoints());
    432    fInner.setIsVolatile(true);
    433    // TODO : write a common error function used by stroking and filling
    434    // The '4' below matches the fill scan converter's error term
    435    fInvResScale = SkScalarInvert(resScale * 4);
    436    fInvResScaleSquared = fInvResScale * fInvResScale;
    437    fRecursionDepth = 0;
    438 }
    439 
    440 void SkPathStroker::moveTo(const SkPoint& pt) {
    441    if (fSegmentCount > 0) {
    442        this->finishContour(false, false);
    443    }
    444    fSegmentCount = 0;
    445    fFirstPt = fPrevPt = pt;
    446    fJoinCompleted = false;
    447 }
    448 
    449 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
    450    fOuter.lineTo(currPt + normal);
    451    fInner.lineTo(currPt - normal);
    452 }
    453 
    454 static bool has_valid_tangent(const SkPath::Iter* iter) {
    455    SkPath::Iter copy = *iter;
    456    while (auto rec = copy.next()) {
    457        SkSpan<const SkPoint> pts = rec->fPoints;
    458        switch (rec->fVerb) {
    459            case SkPathVerb::kMove:
    460                return false;
    461            case SkPathVerb::kLine:
    462                if (pts[0] == pts[1]) {
    463                    continue;
    464                }
    465                return true;
    466            case SkPathVerb::kQuad:
    467            case SkPathVerb::kConic:
    468                if (pts[0] == pts[1] && pts[0] == pts[2]) {
    469                    continue;
    470                }
    471                return true;
    472            case SkPathVerb::kCubic:
    473                if (pts[0] == pts[1] && pts[0] == pts[2] && pts[0] == pts[3]) {
    474                    continue;
    475                }
    476                return true;
    477            case SkPathVerb::kClose:
    478                return false;
    479        }
    480    }
    481    return false;
    482 }
    483 
    484 void SkPathStroker::lineTo(const SkPoint& currPt, const SkPath::Iter* iter) {
    485    bool teenyLine = SkPointPriv::EqualsWithinTolerance(fPrevPt, currPt, SK_ScalarNearlyZero * fInvResScale);
    486    if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper && teenyLine) {
    487        return;
    488    }
    489    if (teenyLine && (fJoinCompleted || (iter && has_valid_tangent(iter)))) {
    490        return;
    491    }
    492    SkVector    normal, unitNormal;
    493 
    494    if (!this->preJoinTo(currPt, &normal, &unitNormal, true)) {
    495        return;
    496    }
    497    this->line_to(currPt, normal);
    498    this->postJoinTo(currPt, normal, unitNormal);
    499 }
    500 
    501 void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& normalAB,
    502        const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) {
    503    if (!set_normal_unitnormal(quad[1], quad[2], fResScale, fRadius, normalBC, unitNormalBC)) {
    504        *normalBC = normalAB;
    505        *unitNormalBC = unitNormalAB;
    506    }
    507 }
    508 
    509 void SkPathStroker::setConicEndNormal(const SkConic& conic, const SkVector& normalAB,
    510        const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) {
    511    setQuadEndNormal(conic.fPts, normalAB, unitNormalAB, normalBC, unitNormalBC);
    512 }
    513 
    514 void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& normalAB,
    515        const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD) {
    516    SkVector    ab = cubic[1] - cubic[0];
    517    SkVector    cd = cubic[3] - cubic[2];
    518 
    519    bool    degenerateAB = degenerate_vector(ab);
    520    bool    degenerateCD = degenerate_vector(cd);
    521 
    522    if (degenerateAB && degenerateCD) {
    523        goto DEGENERATE_NORMAL;
    524    }
    525 
    526    if (degenerateAB) {
    527        ab = cubic[2] - cubic[0];
    528        degenerateAB = degenerate_vector(ab);
    529    }
    530    if (degenerateCD) {
    531        cd = cubic[3] - cubic[1];
    532        degenerateCD = degenerate_vector(cd);
    533    }
    534    if (degenerateAB || degenerateCD) {
    535 DEGENERATE_NORMAL:
    536        *normalCD = normalAB;
    537        *unitNormalCD = unitNormalAB;
    538        return;
    539    }
    540    SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
    541 }
    542 
    543 void SkPathStroker::init(StrokeType strokeType, SkQuadConstruct* quadPts, SkScalar tStart,
    544        SkScalar tEnd) {
    545    fStrokeType = strokeType;
    546    fFoundTangents = false;
    547    quadPts->init(tStart, tEnd);
    548 }
    549 
    550 // returns the distance squared from the point to the line
    551 static SkScalar pt_to_line(const SkPoint& pt, const SkPoint& lineStart, const SkPoint& lineEnd) {
    552    SkVector dxy = lineEnd - lineStart;
    553    SkVector ab0 = pt - lineStart;
    554    SkScalar numer = dxy.dot(ab0);
    555    SkScalar denom = dxy.dot(dxy);
    556    SkScalar t = sk_ieee_float_divide(numer, denom);
    557    if (t >= 0 && t <= 1) {
    558        SkPoint hit = lineStart * (1 - t) + lineEnd * t;
    559        return SkPointPriv::DistanceToSqd(hit, pt);
    560    } else {
    561        return SkPointPriv::DistanceToSqd(pt, lineStart);
    562    }
    563 }
    564 
    565 // returns the distance squared from the point to the line
    566 static SkScalar pt_to_tangent_line(const SkPoint& pt,
    567                                   const SkPoint& lineStart,
    568                                   const SkVector& tangent) {
    569    SkVector dxy = tangent;
    570    SkVector ab0 = pt - lineStart;
    571    SkScalar numer = dxy.dot(ab0);
    572    SkScalar denom = dxy.dot(dxy);
    573    SkScalar t = sk_ieee_float_divide(numer, denom);
    574    if (t >= 0 && t <= 1) {
    575        SkPoint hit = lineStart + tangent * t;
    576        return SkPointPriv::DistanceToSqd(hit, pt);
    577    } else {
    578        return SkPointPriv::DistanceToSqd(pt, lineStart);
    579    }
    580 }
    581 
    582 /*  Given a cubic, determine if all four points are in a line.
    583    Return true if the inner points is close to a line connecting the outermost points.
    584 
    585    Find the outermost point by looking for the largest difference in X or Y.
    586    Given the indices of the outermost points, and that outer_1 is greater than outer_2,
    587    this table shows the index of the smaller of the remaining points:
    588 
    589                      outer_2
    590                  0    1    2    3
    591      outer_1     ----------------
    592         0     |  -    2    1    1
    593         1     |  -    -    0    0
    594         2     |  -    -    -    0
    595         3     |  -    -    -    -
    596 
    597    If outer_1 == 0 and outer_2 == 1, the smaller of the remaining indices (2 and 3) is 2.
    598 
    599    This table can be collapsed to: (1 + (2 >> outer_2)) >> outer_1
    600 
    601    Given three indices (outer_1 outer_2 mid_1) from 0..3, the remaining index is:
    602 
    603               mid_2 == (outer_1 ^ outer_2 ^ mid_1)
    604 */
    605 static bool cubic_in_line(const SkPoint cubic[4]) {
    606    SkScalar ptMax = -1;
    607    int outer1 SK_INIT_TO_AVOID_WARNING;
    608    int outer2 SK_INIT_TO_AVOID_WARNING;
    609    for (int index = 0; index < 3; ++index) {
    610        for (int inner = index + 1; inner < 4; ++inner) {
    611            SkVector testDiff = cubic[inner] - cubic[index];
    612            SkScalar testMax = std::max(SkScalarAbs(testDiff.fX), SkScalarAbs(testDiff.fY));
    613            if (ptMax < testMax) {
    614                outer1 = index;
    615                outer2 = inner;
    616                ptMax = testMax;
    617            }
    618        }
    619    }
    620    SkASSERT(outer1 >= 0 && outer1 <= 2);
    621    SkASSERT(outer2 >= 1 && outer2 <= 3);
    622    SkASSERT(outer1 < outer2);
    623    int mid1 = (1 + (2 >> outer2)) >> outer1;
    624    SkASSERT(mid1 >= 0 && mid1 <= 2);
    625    SkASSERT(outer1 != mid1 && outer2 != mid1);
    626    int mid2 = outer1 ^ outer2 ^ mid1;
    627    SkASSERT(mid2 >= 1 && mid2 <= 3);
    628    SkASSERT(mid2 != outer1 && mid2 != outer2 && mid2 != mid1);
    629    SkASSERT(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f);
    630    SkScalar lineSlop = ptMax * ptMax * 0.00001f;  // this multiplier is pulled out of the air
    631    return pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= lineSlop
    632            && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= lineSlop;
    633 }
    634 
    635 /* Given quad, see if all there points are in a line.
    636   Return true if the inside point is close to a line connecting the outermost points.
    637 
    638   Find the outermost point by looking for the largest difference in X or Y.
    639   Since the XOR of the indices is 3  (0 ^ 1 ^ 2)
    640   the missing index equals: outer_1 ^ outer_2 ^ 3
    641 */
    642 static bool quad_in_line(const SkPoint quad[3]) {
    643    SkScalar ptMax = -1;
    644    int outer1 SK_INIT_TO_AVOID_WARNING;
    645    int outer2 SK_INIT_TO_AVOID_WARNING;
    646    for (int index = 0; index < 2; ++index) {
    647        for (int inner = index + 1; inner < 3; ++inner) {
    648            SkVector testDiff = quad[inner] - quad[index];
    649            SkScalar testMax = std::max(SkScalarAbs(testDiff.fX), SkScalarAbs(testDiff.fY));
    650            if (ptMax < testMax) {
    651                outer1 = index;
    652                outer2 = inner;
    653                ptMax = testMax;
    654            }
    655        }
    656    }
    657    SkASSERT(outer1 >= 0 && outer1 <= 1);
    658    SkASSERT(outer2 >= 1 && outer2 <= 2);
    659    SkASSERT(outer1 < outer2);
    660    int mid = outer1 ^ outer2 ^ 3;
    661    const float kCurvatureSlop = 0.000005f;  // this multiplier is pulled out of the air
    662    SkScalar lineSlop =  ptMax * ptMax * kCurvatureSlop;
    663    return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop;
    664 }
    665 
    666 static bool conic_in_line(const SkConic& conic) {
    667    return quad_in_line(conic.fPts);
    668 }
    669 
    670 SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic[4],
    671        SkPoint reduction[3], const SkPoint** tangentPtPtr) {
    672    bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]);
    673    bool degenerateBC = degenerate_vector(cubic[2] - cubic[1]);
    674    bool degenerateCD = degenerate_vector(cubic[3] - cubic[2]);
    675    if (degenerateAB & degenerateBC & degenerateCD) {
    676        return kPoint_ReductionType;
    677    }
    678    if (degenerateAB + degenerateBC + degenerateCD == 2) {
    679        return kLine_ReductionType;
    680    }
    681    if (!cubic_in_line(cubic)) {
    682        *tangentPtPtr = degenerateAB ? &cubic[2] : &cubic[1];
    683        return kQuad_ReductionType;
    684    }
    685    SkScalar tValues[3];
    686    int count = SkFindCubicMaxCurvature(cubic, tValues);
    687    int rCount = 0;
    688    // Now loop over the t-values, and reject any that evaluate to either end-point
    689    for (int index = 0; index < count; ++index) {
    690        SkScalar t = tValues[index];
    691        if (0 >= t || t >= 1) {
    692            continue;
    693        }
    694        SkEvalCubicAt(cubic, t, &reduction[rCount], nullptr, nullptr);
    695        if (reduction[rCount] != cubic[0] && reduction[rCount] != cubic[3]) {
    696            ++rCount;
    697        }
    698    }
    699    if (rCount == 0) {
    700        return kLine_ReductionType;
    701    }
    702    static_assert(kQuad_ReductionType + 1 == kDegenerate_ReductionType, "enum_out_of_whack");
    703    static_assert(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, "enum_out_of_whack");
    704    static_assert(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, "enum_out_of_whack");
    705 
    706    return (ReductionType) (kQuad_ReductionType + rCount);
    707 }
    708 
    709 SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(const SkConic& conic,
    710        SkPoint* reduction) {
    711    bool degenerateAB = degenerate_vector(conic.fPts[1] - conic.fPts[0]);
    712    bool degenerateBC = degenerate_vector(conic.fPts[2] - conic.fPts[1]);
    713    if (degenerateAB & degenerateBC) {
    714        return kPoint_ReductionType;
    715    }
    716    if (degenerateAB | degenerateBC) {
    717        return kLine_ReductionType;
    718    }
    719    if (!conic_in_line(conic)) {
    720        return kQuad_ReductionType;
    721    }
    722    // SkFindConicMaxCurvature would be a better solution, once we know how to
    723    // implement it. Quad curvature is a reasonable substitute
    724    SkScalar t = SkFindQuadMaxCurvature(conic.fPts);
    725    if (0 == t || SkIsNaN(t)) {
    726        return kLine_ReductionType;
    727    }
    728    conic.evalAt(t, reduction, nullptr);
    729    return kDegenerate_ReductionType;
    730 }
    731 
    732 SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3],
    733        SkPoint* reduction) {
    734    bool degenerateAB = degenerate_vector(quad[1] - quad[0]);
    735    bool degenerateBC = degenerate_vector(quad[2] - quad[1]);
    736    if (degenerateAB & degenerateBC) {
    737        return kPoint_ReductionType;
    738    }
    739    if (degenerateAB | degenerateBC) {
    740        return kLine_ReductionType;
    741    }
    742    if (!quad_in_line(quad)) {
    743        return kQuad_ReductionType;
    744    }
    745    SkScalar t = SkFindQuadMaxCurvature(quad);
    746    if (0 == t || 1 == t) {
    747        return kLine_ReductionType;
    748    }
    749    *reduction = SkEvalQuadAt(quad, t);
    750    return kDegenerate_ReductionType;
    751 }
    752 
    753 void SkPathStroker::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
    754    const SkConic conic(fPrevPt, pt1, pt2, weight);
    755    SkPoint reduction;
    756    ReductionType reductionType = CheckConicLinear(conic, &reduction);
    757    if (kPoint_ReductionType == reductionType) {
    758        /* If the stroke consists of a moveTo followed by a degenerate curve, treat it
    759            as if it were followed by a zero-length line. Lines without length
    760            can have square and round end caps. */
    761        this->lineTo(pt2);
    762        return;
    763    }
    764    if (kLine_ReductionType == reductionType) {
    765        this->lineTo(pt2);
    766        return;
    767    }
    768    if (kDegenerate_ReductionType == reductionType) {
    769        this->lineTo(reduction);
    770        SkStrokerPriv::JoinProc saveJoiner = fJoiner;
    771        fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
    772        this->lineTo(pt2);
    773        fJoiner = saveJoiner;
    774        return;
    775    }
    776    SkASSERT(kQuad_ReductionType == reductionType);
    777    SkVector normalAB, unitAB, normalBC, unitBC;
    778    if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) {
    779        this->lineTo(pt2);
    780        return;
    781    }
    782    SkQuadConstruct quadPts;
    783    this->init(kOuter_StrokeType, &quadPts, 0, 1);
    784    (void) this->conicStroke(conic, &quadPts);
    785    this->init(kInner_StrokeType, &quadPts, 0, 1);
    786    (void) this->conicStroke(conic, &quadPts);
    787    this->setConicEndNormal(conic, normalAB, unitAB, &normalBC, &unitBC);
    788    this->postJoinTo(pt2, normalBC, unitBC);
    789 }
    790 
    791 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
    792    const SkPoint quad[3] = { fPrevPt, pt1, pt2 };
    793    SkPoint reduction;
    794    ReductionType reductionType = CheckQuadLinear(quad, &reduction);
    795    if (kPoint_ReductionType == reductionType) {
    796        /* If the stroke consists of a moveTo followed by a degenerate curve, treat it
    797            as if it were followed by a zero-length line. Lines without length
    798            can have square and round end caps. */
    799        this->lineTo(pt2);
    800        return;
    801    }
    802    if (kLine_ReductionType == reductionType) {
    803        this->lineTo(pt2);
    804        return;
    805    }
    806    if (kDegenerate_ReductionType == reductionType) {
    807        this->lineTo(reduction);
    808        SkStrokerPriv::JoinProc saveJoiner = fJoiner;
    809        fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
    810        this->lineTo(pt2);
    811        fJoiner = saveJoiner;
    812        return;
    813    }
    814    SkASSERT(kQuad_ReductionType == reductionType);
    815    SkVector normalAB, unitAB, normalBC, unitBC;
    816    if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) {
    817        this->lineTo(pt2);
    818        return;
    819    }
    820    SkQuadConstruct quadPts;
    821    this->init(kOuter_StrokeType, &quadPts, 0, 1);
    822    (void) this->quadStroke(quad, &quadPts);
    823    this->init(kInner_StrokeType, &quadPts, 0, 1);
    824    (void) this->quadStroke(quad, &quadPts);
    825    this->setQuadEndNormal(quad, normalAB, unitAB, &normalBC, &unitBC);
    826 
    827    this->postJoinTo(pt2, normalBC, unitBC);
    828 }
    829 
    830 // Given a point on the curve and its derivative, scale the derivative by the radius, and
    831 // compute the perpendicular point and its tangent.
    832 void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt,
    833        SkVector* tangent) const {
    834    if (!dxy->setLength(fRadius)) {
    835        dxy->set(fRadius, 0);
    836    }
    837    SkScalar axisFlip = SkIntToScalar(fStrokeType);  // go opposite ways for outer, inner
    838    onPt->fX = tPt.fX + axisFlip * dxy->fY;
    839    onPt->fY = tPt.fY - axisFlip * dxy->fX;
    840    if (tangent) {
    841        *tangent = *dxy;
    842    }
    843 }
    844 
    845 // Given a conic and t, return the point on curve, its perpendicular, and the perpendicular tangent.
    846 // Returns false if the perpendicular could not be computed (because the derivative collapsed to 0)
    847 void SkPathStroker::conicPerpRay(const SkConic& conic, SkScalar t, SkPoint* tPt, SkPoint* onPt,
    848        SkVector* tangent) const {
    849    SkVector dxy;
    850    conic.evalAt(t, tPt, &dxy);
    851    if (dxy.isZero()) {
    852        dxy = conic.fPts[2] - conic.fPts[0];
    853    }
    854    this->setRayPts(*tPt, &dxy, onPt, tangent);
    855 }
    856 
    857 // Given a conic and a t range, find the start and end if they haven't been found already.
    858 void SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts) const {
    859    if (!quadPts->fStartSet) {
    860        SkPoint conicStartPt;
    861        this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts->fQuad[0],
    862                &quadPts->fTangentStart);
    863        quadPts->fStartSet = true;
    864    }
    865    if (!quadPts->fEndSet) {
    866        SkPoint conicEndPt;
    867        this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQuad[2],
    868                &quadPts->fTangentEnd);
    869        quadPts->fEndSet = true;
    870    }
    871 }
    872 
    873 
    874 // Given a cubic and t, return the point on curve, its perpendicular, and the perpendicular tangent.
    875 void SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt,
    876        SkVector* tangent) const {
    877    SkVector dxy;
    878    SkPoint chopped[7];
    879    SkEvalCubicAt(cubic, t, tPt, &dxy, nullptr);
    880    if (dxy.isZero()) {
    881        const SkPoint* cPts = cubic;
    882        if (SkScalarNearlyZero(t)) {
    883            dxy = cubic[2] - cubic[0];
    884        } else if (SkScalarNearlyZero(1 - t)) {
    885            dxy = cubic[3] - cubic[1];
    886        } else {
    887            // If the cubic inflection falls on the cusp, subdivide the cubic
    888            // to find the tangent at that point.
    889            SkChopCubicAt(cubic, chopped, t);
    890            dxy = chopped[3] - chopped[2];
    891            if (dxy.isZero()) {
    892                dxy = chopped[3] - chopped[1];
    893                cPts = chopped;
    894            }
    895        }
    896        if (dxy.isZero()) {
    897            dxy = cPts[3] - cPts[0];
    898        }
    899    }
    900    setRayPts(*tPt, &dxy, onPt, tangent);
    901 }
    902 
    903 // Given a cubic and a t range, find the start and end if they haven't been found already.
    904 void SkPathStroker::cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* quadPts) {
    905    if (!quadPts->fStartSet) {
    906        SkPoint cubicStartPt;
    907        this->cubicPerpRay(cubic, quadPts->fStartT, &cubicStartPt, &quadPts->fQuad[0],
    908                &quadPts->fTangentStart);
    909        quadPts->fStartSet = true;
    910    }
    911    if (!quadPts->fEndSet) {
    912        SkPoint cubicEndPt;
    913        this->cubicPerpRay(cubic, quadPts->fEndT, &cubicEndPt, &quadPts->fQuad[2],
    914                &quadPts->fTangentEnd);
    915        quadPts->fEndSet = true;
    916    }
    917 }
    918 
    919 void SkPathStroker::cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* quadPts,
    920        SkPoint* mid) const {
    921    SkPoint cubicMidPt;
    922    this->cubicPerpRay(cubic, quadPts->fMidT, &cubicMidPt, mid, nullptr);
    923 }
    924 
    925 // Given a quad and t, return the point on curve, its perpendicular, and the perpendicular tangent.
    926 void SkPathStroker::quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt,
    927        SkVector* tangent) const {
    928    SkVector dxy;
    929    SkEvalQuadAt(quad, t, tPt, &dxy);
    930    if (dxy.isZero()) {
    931        dxy = quad[2] - quad[0];
    932    }
    933    setRayPts(*tPt, &dxy, onPt, tangent);
    934 }
    935 
    936 // Find the intersection of the stroke tangents to construct a stroke quad.
    937 // Return whether the stroke is a degenerate (a line), a quad, or must be split.
    938 // Optionally compute the quad's control point.
    939 SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts,
    940        IntersectRayType intersectRayType  STROKER_DEBUG_PARAMS(int depth)) const {
    941    const SkPoint& start = quadPts->fQuad[0];
    942    const SkPoint& end = quadPts->fQuad[2];
    943    SkVector aLen = quadPts->fTangentStart;
    944    SkVector bLen = quadPts->fTangentEnd;
    945    /* Slopes match when denom goes to zero:
    946                      axLen / ayLen ==                   bxLen / byLen
    947    (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
    948             byLen  * axLen         ==  ayLen          * bxLen
    949             byLen  * axLen         -   ayLen          * bxLen         ( == denom )
    950     */
    951    SkScalar denom = aLen.cross(bLen);
    952    if (denom == 0 || !SkIsFinite(denom)) {
    953        quadPts->fOppositeTangents = aLen.dot(bLen) < 0;
    954        return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, "denom == 0");
    955    }
    956    quadPts->fOppositeTangents = false;
    957    SkVector ab0 = start - end;
    958    SkScalar numerA = bLen.cross(ab0);
    959    SkScalar numerB = aLen.cross(ab0);
    960    if ((numerA >= 0) == (numerB >= 0)) { // if the control point is outside the quad ends
    961        // if the perpendicular distances from the quad points to the opposite tangent line
    962        // are small, a straight line is good enough
    963        SkScalar dist1 = pt_to_tangent_line(start, end, quadPts->fTangentEnd);
    964        SkScalar dist2 = pt_to_tangent_line(end, start, quadPts->fTangentStart);
    965        if (std::max(dist1, dist2) <= fInvResScaleSquared) {
    966            return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts,
    967                    "std::max(dist1=%g, dist2=%g) <= fInvResScaleSquared", dist1, dist2);
    968        }
    969        return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
    970                "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB);
    971    }
    972    // check to see if the denominator is teeny relative to the numerator
    973    // if the offset by one will be lost, the ratio is too large
    974    numerA /= denom;
    975    bool validDivide = numerA > numerA - 1;
    976    if (validDivide) {
    977        if (kCtrlPt_RayType == intersectRayType) {
    978            SkPoint* ctrlPt = &quadPts->fQuad[1];
    979            // the intersection of the tangents need not be on the tangent segment
    980            // so 0 <= numerA <= 1 is not necessarily true
    981            *ctrlPt = start + quadPts->fTangentStart * numerA;
    982        }
    983        return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
    984                "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB);
    985    }
    986    quadPts->fOppositeTangents = aLen.dot(bLen) < 0;
    987    // if the lines are parallel, straight line is good enough
    988    return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts,
    989            "SkScalarNearlyZero(denom=%g)", denom);
    990 }
    991 
    992 // Given a cubic and a t-range, determine if the stroke can be described by a quadratic.
    993 SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4],
    994        SkQuadConstruct* quadPts) {
    995    this->cubicQuadEnds(cubic, quadPts);
    996    return this->intersectRay(quadPts, kResultType_RayType  STROKER_DEBUG_PARAMS(fRecursionDepth));
    997 }
    998 
    999 // Intersect the line with the quad and return the t values on the quad where the line crosses.
   1000 static int intersect_quad_ray(const SkPoint line[2], const SkPoint quad[3], SkScalar roots[2]) {
   1001    SkVector vec = line[1] - line[0];
   1002    SkScalar r[3];
   1003    for (int n = 0; n < 3; ++n) {
   1004        r[n] = vec.cross(quad[n] - line[0]);
   1005    }
   1006    SkScalar A = r[2];
   1007    SkScalar B = r[1];
   1008    SkScalar C = r[0];
   1009    A += C - 2 * B;  // A = a - 2*b + c
   1010    B -= C;  // B = -(b - c)
   1011    return SkFindUnitQuadRoots(A, 2 * B, C, roots);
   1012 }
   1013 
   1014 // Return true if the point is close to the bounds of the quad. This is used as a quick reject.
   1015 bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const {
   1016    SkScalar xMin = std::min({quad[0].fX, quad[1].fX, quad[2].fX});
   1017    if (pt.fX + fInvResScale < xMin) {
   1018        return false;
   1019    }
   1020    SkScalar xMax = std::max({quad[0].fX, quad[1].fX, quad[2].fX});
   1021    if (pt.fX - fInvResScale > xMax) {
   1022        return false;
   1023    }
   1024    SkScalar yMin = std::min({quad[0].fY, quad[1].fY, quad[2].fY});
   1025    if (pt.fY + fInvResScale < yMin) {
   1026        return false;
   1027    }
   1028    SkScalar yMax = std::max({quad[0].fY, quad[1].fY, quad[2].fY});
   1029    if (pt.fY - fInvResScale > yMax) {
   1030        return false;
   1031    }
   1032    return true;
   1033 }
   1034 
   1035 static bool points_within_dist(const SkPoint& nearPt, const SkPoint& farPt, SkScalar limit) {
   1036    return SkPointPriv::DistanceToSqd(nearPt, farPt) <= limit * limit;
   1037 }
   1038 
   1039 static bool sharp_angle(const SkPoint quad[3]) {
   1040    SkVector smaller = quad[1] - quad[0];
   1041    SkVector larger = quad[1] - quad[2];
   1042    SkScalar smallerLen = SkPointPriv::LengthSqd(smaller);
   1043    SkScalar largerLen = SkPointPriv::LengthSqd(larger);
   1044    if (smallerLen > largerLen) {
   1045        using std::swap;
   1046        swap(smaller, larger);
   1047        largerLen = smallerLen;
   1048    }
   1049    if (!smaller.setLength(largerLen)) {
   1050        return false;
   1051    }
   1052    SkScalar dot = smaller.dot(larger);
   1053    return dot > 0;
   1054 }
   1055 
   1056 SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[3],
   1057        const SkPoint ray[2], SkQuadConstruct* quadPts  STROKER_DEBUG_PARAMS(int depth)) const {
   1058    SkPoint strokeMid = SkEvalQuadAt(stroke, SK_ScalarHalf);
   1059    // measure the distance from the curve to the quad-stroke midpoint, compare to radius
   1060    if (points_within_dist(ray[0], strokeMid, fInvResScale)) {  // if the difference is small
   1061        if (sharp_angle(quadPts->fQuad)) {
   1062            return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
   1063                    "sharp_angle (1) =%g,%g, %g,%g, %g,%g",
   1064                    quadPts->fQuad[0].fX, quadPts->fQuad[0].fY,
   1065                    quadPts->fQuad[1].fX, quadPts->fQuad[1].fY,
   1066                    quadPts->fQuad[2].fX, quadPts->fQuad[2].fY);
   1067        }
   1068        return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
   1069                "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fInvResScale=%g)",
   1070                ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY, fInvResScale);
   1071    }
   1072    // measure the distance to quad's bounds (quick reject)
   1073        // an alternative : look for point in triangle
   1074    if (!ptInQuadBounds(stroke, ray[0])) {  // if far, subdivide
   1075        return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
   1076                "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)",
   1077                stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY,
   1078                ray[0].fX, ray[0].fY);
   1079    }
   1080    // measure the curve ray distance to the quad-stroke
   1081    SkScalar roots[2];
   1082    int rootCount = intersect_quad_ray(ray, stroke, roots);
   1083    if (rootCount != 1) {
   1084        return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
   1085                "rootCount=%d != 1", rootCount);
   1086    }
   1087    SkPoint quadPt = SkEvalQuadAt(stroke, roots[0]);
   1088    SkScalar error = fInvResScale * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2);
   1089    if (points_within_dist(ray[0], quadPt, error)) {  // if the difference is small, we're done
   1090        if (sharp_angle(quadPts->fQuad)) {
   1091            return STROKER_RESULT(kSplit_ResultType, depth, quadPts,
   1092                    "sharp_angle (2) =%g,%g, %g,%g, %g,%g",
   1093                    quadPts->fQuad[0].fX, quadPts->fQuad[0].fY,
   1094                    quadPts->fQuad[1].fX, quadPts->fQuad[1].fY,
   1095                    quadPts->fQuad[2].fX, quadPts->fQuad[2].fY);
   1096        }
   1097        return STROKER_RESULT(kQuad_ResultType, depth, quadPts,
   1098                "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)",
   1099                ray[0].fX, ray[0].fY, quadPt.fX, quadPt.fY, error);
   1100    }
   1101    // otherwise, subdivide
   1102    return STROKER_RESULT(kSplit_ResultType, depth, quadPts, "%s", "fall through");
   1103 }
   1104 
   1105 SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4],
   1106        SkQuadConstruct* quadPts) {
   1107    // get the quadratic approximation of the stroke
   1108    this->cubicQuadEnds(cubic, quadPts);
   1109    ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType
   1110            STROKER_DEBUG_PARAMS(fRecursionDepth) );
   1111    if (resultType != kQuad_ResultType) {
   1112        return resultType;
   1113    }
   1114    // project a ray from the curve to the stroke
   1115    SkPoint ray[2];  // points near midpoint on quad, midpoint on cubic
   1116    this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], nullptr);
   1117    return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts
   1118            STROKER_DEBUG_PARAMS(fRecursionDepth));
   1119 }
   1120 
   1121 SkPathStroker::ResultType SkPathStroker::compareQuadConic(const SkConic& conic,
   1122        SkQuadConstruct* quadPts) const {
   1123    // get the quadratic approximation of the stroke
   1124    this->conicQuadEnds(conic, quadPts);
   1125    ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType
   1126            STROKER_DEBUG_PARAMS(fRecursionDepth) );
   1127    if (resultType != kQuad_ResultType) {
   1128        return resultType;
   1129    }
   1130    // project a ray from the curve to the stroke
   1131    SkPoint ray[2];  // points near midpoint on quad, midpoint on conic
   1132    this->conicPerpRay(conic, quadPts->fMidT, &ray[1], &ray[0], nullptr);
   1133    return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts
   1134            STROKER_DEBUG_PARAMS(fRecursionDepth));
   1135 }
   1136 
   1137 SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3],
   1138        SkQuadConstruct* quadPts) {
   1139    // get the quadratic approximation of the stroke
   1140    if (!quadPts->fStartSet) {
   1141        SkPoint quadStartPt;
   1142        this->quadPerpRay(quad, quadPts->fStartT, &quadStartPt, &quadPts->fQuad[0],
   1143                &quadPts->fTangentStart);
   1144        quadPts->fStartSet = true;
   1145    }
   1146    if (!quadPts->fEndSet) {
   1147        SkPoint quadEndPt;
   1148        this->quadPerpRay(quad, quadPts->fEndT, &quadEndPt, &quadPts->fQuad[2],
   1149                &quadPts->fTangentEnd);
   1150        quadPts->fEndSet = true;
   1151    }
   1152    ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType
   1153            STROKER_DEBUG_PARAMS(fRecursionDepth));
   1154    if (resultType != kQuad_ResultType) {
   1155        return resultType;
   1156    }
   1157    // project a ray from the curve to the stroke
   1158    SkPoint ray[2];
   1159    this->quadPerpRay(quad, quadPts->fMidT, &ray[1], &ray[0], nullptr);
   1160    return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts
   1161            STROKER_DEBUG_PARAMS(fRecursionDepth));
   1162 }
   1163 
   1164 void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) {
   1165    const SkPoint* quad = quadPts->fQuad;
   1166    auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
   1167    sink->lineTo(quad[2]);
   1168 }
   1169 
   1170 bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* quadPts) const {
   1171    SkPoint strokeMid;
   1172    this->cubicQuadMid(cubic, quadPts, &strokeMid);
   1173    SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]);
   1174    return dist < fInvResScaleSquared;
   1175 }
   1176 
   1177 bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts) {
   1178    if (!fFoundTangents) {
   1179        ResultType resultType = this->tangentsMeet(cubic, quadPts);
   1180        if (kQuad_ResultType != resultType) {
   1181            if ((kDegenerate_ResultType == resultType
   1182                    || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2],
   1183                    fInvResScale)) && cubicMidOnLine(cubic, quadPts)) {
   1184                addDegenerateLine(quadPts);
   1185                DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1186                return true;
   1187            }
   1188        } else {
   1189            fFoundTangents = true;
   1190        }
   1191    }
   1192    if (fFoundTangents) {
   1193        ResultType resultType = this->compareQuadCubic(cubic, quadPts);
   1194        if (kQuad_ResultType == resultType) {
   1195            auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
   1196            const SkPoint* stroke = quadPts->fQuad;
   1197            sink->quadTo(stroke[1], stroke[2]);
   1198            DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1199            return true;
   1200        }
   1201        if (kDegenerate_ResultType == resultType) {
   1202            if (!quadPts->fOppositeTangents) {
   1203              addDegenerateLine(quadPts);
   1204              DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1205              return true;
   1206            }
   1207        }
   1208    }
   1209    if (!quadPts->fQuad[2].isFinite()) {
   1210        DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1211        return false;  // just abort if projected quad isn't representable
   1212    }
   1213 #if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
   1214    SkDEBUGCODE(gMaxRecursion[fFoundTangents] = std::max(gMaxRecursion[fFoundTangents],
   1215            fRecursionDepth + 1));
   1216 #endif
   1217    if (++fRecursionDepth > kRecursiveLimits[fFoundTangents]) {
   1218        DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1219        // If we stop making progress, just emit a line and move on
   1220        addDegenerateLine(quadPts);
   1221        return true;
   1222    }
   1223    SkQuadConstruct half;
   1224    if (!half.initWithStart(quadPts)) {
   1225        addDegenerateLine(quadPts);
   1226        DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1227        --fRecursionDepth;
   1228        return true;
   1229    }
   1230    if (!this->cubicStroke(cubic, &half)) {
   1231        return false;
   1232    }
   1233    if (!half.initWithEnd(quadPts)) {
   1234        addDegenerateLine(quadPts);
   1235        DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth);
   1236        --fRecursionDepth;
   1237        return true;
   1238    }
   1239    if (!this->cubicStroke(cubic, &half)) {
   1240        return false;
   1241    }
   1242    --fRecursionDepth;
   1243    return true;
   1244 }
   1245 
   1246 bool SkPathStroker::conicStroke(const SkConic& conic, SkQuadConstruct* quadPts) {
   1247    ResultType resultType = this->compareQuadConic(conic, quadPts);
   1248    if (kQuad_ResultType == resultType) {
   1249        const SkPoint* stroke = quadPts->fQuad;
   1250        auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
   1251        sink->quadTo(stroke[1], stroke[2]);
   1252        return true;
   1253    }
   1254    if (kDegenerate_ResultType == resultType) {
   1255        addDegenerateLine(quadPts);
   1256        return true;
   1257    }
   1258 #if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
   1259    SkDEBUGCODE(gMaxRecursion[kConic_RecursiveLimit] = std::max(gMaxRecursion[kConic_RecursiveLimit],
   1260            fRecursionDepth + 1));
   1261 #endif
   1262    if (++fRecursionDepth > kRecursiveLimits[kConic_RecursiveLimit]) {
   1263        // If we stop making progress, just emit a line and move on
   1264        addDegenerateLine(quadPts);
   1265        return true;
   1266    }
   1267    SkQuadConstruct half;
   1268    (void) half.initWithStart(quadPts);
   1269    if (!this->conicStroke(conic, &half)) {
   1270        return false;
   1271    }
   1272    (void) half.initWithEnd(quadPts);
   1273    if (!this->conicStroke(conic, &half)) {
   1274        return false;
   1275    }
   1276    --fRecursionDepth;
   1277    return true;
   1278 }
   1279 
   1280 bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts) {
   1281    ResultType resultType = this->compareQuadQuad(quad, quadPts);
   1282    if (kQuad_ResultType == resultType) {
   1283        const SkPoint* stroke = quadPts->fQuad;
   1284        auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner;
   1285        sink->quadTo(stroke[1], stroke[2]);
   1286        return true;
   1287    }
   1288    if (kDegenerate_ResultType == resultType) {
   1289        addDegenerateLine(quadPts);
   1290        return true;
   1291    }
   1292 #if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING
   1293    SkDEBUGCODE(gMaxRecursion[kQuad_RecursiveLimit] = std::max(gMaxRecursion[kQuad_RecursiveLimit],
   1294            fRecursionDepth + 1));
   1295 #endif
   1296    if (++fRecursionDepth > kRecursiveLimits[kQuad_RecursiveLimit]) {
   1297        // If we stop making progress, just emit a line and move on
   1298        addDegenerateLine(quadPts);
   1299        return true;
   1300    }
   1301    SkQuadConstruct half;
   1302    (void) half.initWithStart(quadPts);
   1303    if (!this->quadStroke(quad, &half)) {
   1304        return false;
   1305    }
   1306    (void) half.initWithEnd(quadPts);
   1307    if (!this->quadStroke(quad, &half)) {
   1308        return false;
   1309    }
   1310    --fRecursionDepth;
   1311    return true;
   1312 }
   1313 
   1314 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
   1315                            const SkPoint& pt3) {
   1316    const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 };
   1317    SkPoint reduction[3];
   1318    const SkPoint* tangentPt;
   1319    ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt);
   1320    if (kPoint_ReductionType == reductionType) {
   1321        /* If the stroke consists of a moveTo followed by a degenerate curve, treat it
   1322            as if it were followed by a zero-length line. Lines without length
   1323            can have square and round end caps. */
   1324        this->lineTo(pt3);
   1325        return;
   1326    }
   1327    if (kLine_ReductionType == reductionType) {
   1328        this->lineTo(pt3);
   1329        return;
   1330    }
   1331    if (kDegenerate_ReductionType <= reductionType && kDegenerate3_ReductionType >= reductionType) {
   1332        this->lineTo(reduction[0]);
   1333        SkStrokerPriv::JoinProc saveJoiner = fJoiner;
   1334        fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join);
   1335        if (kDegenerate2_ReductionType <= reductionType) {
   1336            this->lineTo(reduction[1]);
   1337        }
   1338        if (kDegenerate3_ReductionType == reductionType) {
   1339            this->lineTo(reduction[2]);
   1340        }
   1341        this->lineTo(pt3);
   1342        fJoiner = saveJoiner;
   1343        return;
   1344    }
   1345    SkASSERT(kQuad_ReductionType == reductionType);
   1346    SkVector normalAB, unitAB, normalCD, unitCD;
   1347    if (!this->preJoinTo(*tangentPt, &normalAB, &unitAB, false)) {
   1348        this->lineTo(pt3);
   1349        return;
   1350    }
   1351    SkScalar tValues[2];
   1352    int count = SkFindCubicInflections(cubic, tValues);
   1353    SkScalar lastT = 0;
   1354    for (int index = 0; index <= count; ++index) {
   1355        SkScalar nextT = index < count ? tValues[index] : 1;
   1356        SkQuadConstruct quadPts;
   1357        this->init(kOuter_StrokeType, &quadPts, lastT, nextT);
   1358        (void) this->cubicStroke(cubic, &quadPts);
   1359        this->init(kInner_StrokeType, &quadPts, lastT, nextT);
   1360        (void) this->cubicStroke(cubic, &quadPts);
   1361        lastT = nextT;
   1362    }
   1363    SkScalar cusp = SkFindCubicCusp(cubic);
   1364    if (cusp > 0) {
   1365        SkPoint cuspLoc;
   1366        SkEvalCubicAt(cubic, cusp, &cuspLoc, nullptr, nullptr);
   1367        fCusper.addCircle(cuspLoc.fX, cuspLoc.fY, fRadius);
   1368    }
   1369    // emit the join even if one stroke succeeded but the last one failed
   1370    // this avoids reversing an inner stroke with a partial path followed by another moveto
   1371    this->setCubicEndNormal(cubic, normalAB, unitAB, &normalCD, &unitCD);
   1372 
   1373    this->postJoinTo(pt3, normalCD, unitCD);
   1374 }
   1375 
   1376 ///////////////////////////////////////////////////////////////////////////////
   1377 ///////////////////////////////////////////////////////////////////////////////
   1378 
   1379 #include "src/core/SkPaintDefaults.h"
   1380 
   1381 SkStroke::SkStroke() {
   1382    fWidth      = SK_Scalar1;
   1383    fMiterLimit = SkPaintDefaults_MiterLimit;
   1384    fResScale   = 1;
   1385    fCap        = SkPaint::kDefault_Cap;
   1386    fJoin       = SkPaint::kDefault_Join;
   1387    fDoFill     = false;
   1388 }
   1389 
   1390 SkStroke::SkStroke(const SkPaint& p) {
   1391    fWidth      = p.getStrokeWidth();
   1392    fMiterLimit = p.getStrokeMiter();
   1393    fResScale   = 1;
   1394    fCap        = (uint8_t)p.getStrokeCap();
   1395    fJoin       = (uint8_t)p.getStrokeJoin();
   1396    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
   1397 }
   1398 
   1399 SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
   1400    fWidth      = width;
   1401    fMiterLimit = p.getStrokeMiter();
   1402    fResScale   = 1;
   1403    fCap        = (uint8_t)p.getStrokeCap();
   1404    fJoin       = (uint8_t)p.getStrokeJoin();
   1405    fDoFill     = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
   1406 }
   1407 
   1408 void SkStroke::setWidth(SkScalar width) {
   1409    SkASSERT(width >= 0);
   1410    fWidth = width;
   1411 }
   1412 
   1413 void SkStroke::setMiterLimit(SkScalar miterLimit) {
   1414    SkASSERT(miterLimit >= 0);
   1415    fMiterLimit = miterLimit;
   1416 }
   1417 
   1418 void SkStroke::setCap(SkPaint::Cap cap) {
   1419    SkASSERT((unsigned)cap < SkPaint::kCapCount);
   1420    fCap = SkToU8(cap);
   1421 }
   1422 
   1423 void SkStroke::setJoin(SkPaint::Join join) {
   1424    SkASSERT((unsigned)join < SkPaint::kJoinCount);
   1425    fJoin = SkToU8(join);
   1426 }
   1427 
   1428 ///////////////////////////////////////////////////////////////////////////////
   1429 
   1430 void SkStroke::strokePath(const SkPath& src, SkPathBuilder* dst) const {
   1431    SkASSERT(dst);
   1432 
   1433    SkScalar radius = SkScalarHalf(fWidth);
   1434 
   1435    if (radius <= 0) {
   1436        return;
   1437    }
   1438 
   1439    // If src is really a rect, call our specialty strokeRect() method
   1440    {
   1441        SkRect rect;
   1442        bool isClosed = false;
   1443        SkPathDirection dir;
   1444        if (src.isRect(&rect, &isClosed, &dir) && isClosed) {
   1445            this->strokeRect(rect, dst, dir);
   1446            // our answer should preserve the inverseness of the src
   1447            if (src.isInverseFillType()) {
   1448                SkASSERT(!dst->isInverseFillType());
   1449                dst->toggleInverseFillType();
   1450            }
   1451            return;
   1452        }
   1453    }
   1454 
   1455    // We can always ignore centers for stroke and fill convex line-only paths
   1456    // TODO: remove the line-only restriction
   1457    bool ignoreCenter = fDoFill && (src.getSegmentMasks() == SkPath::kLine_SegmentMask) &&
   1458                        src.isLastContourClosed() && src.isConvex();
   1459 
   1460    SkPathStroker   stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(),
   1461                            fResScale, ignoreCenter);
   1462 
   1463    SkPath::Iter iter(src, false);
   1464    SkPathVerb   lastSegment = SkPathVerb::kMove;
   1465    while (auto rec = iter.next()) {
   1466        SkSpan<const SkPoint> pts = rec->fPoints;
   1467        switch (rec->fVerb) {
   1468            case SkPathVerb::kMove:
   1469                stroker.moveTo(pts[0]);
   1470                break;
   1471            case SkPathVerb::kLine:
   1472                stroker.lineTo(pts[1], &iter);
   1473                lastSegment = SkPathVerb::kLine;
   1474                break;
   1475            case SkPathVerb::kQuad:
   1476                stroker.quadTo(pts[1], pts[2]);
   1477                lastSegment = SkPathVerb::kQuad;
   1478                break;
   1479            case SkPathVerb::kConic: {
   1480                stroker.conicTo(pts[1], pts[2], rec->conicWeight());
   1481                lastSegment = SkPathVerb::kConic;
   1482            } break;
   1483            case SkPathVerb::kCubic:
   1484                stroker.cubicTo(pts[1], pts[2], pts[3]);
   1485                lastSegment = SkPathVerb::kCubic;
   1486                break;
   1487            case SkPathVerb::kClose:
   1488                if (SkPaint::kButt_Cap != this->getCap()) {
   1489                    /* If the stroke consists of a moveTo followed by a close, treat it
   1490                       as if it were followed by a zero-length line. Lines without length
   1491                       can have square and round end caps. */
   1492                    if (stroker.hasOnlyMoveTo()) {
   1493                        stroker.lineTo(stroker.moveToPt());
   1494                        goto ZERO_LENGTH;
   1495                    }
   1496                    /* If the stroke consists of a moveTo followed by one or more zero-length
   1497                       verbs, then followed by a close, treat is as if it were followed by a
   1498                       zero-length line. Lines without length can have square & round end caps. */
   1499                    if (stroker.isCurrentContourEmpty()) {
   1500                ZERO_LENGTH:
   1501                        lastSegment = SkPathVerb::kLine;
   1502                        break;
   1503                    }
   1504                }
   1505                stroker.close(lastSegment == SkPathVerb::kLine);
   1506                break;
   1507        }
   1508    }
   1509    stroker.done(dst, lastSegment == SkPathVerb::kLine);
   1510 
   1511    if (fDoFill && !ignoreCenter) {
   1512        auto d = SkPathPriv::ComputeFirstDirection(SkPathPriv::Raw(src));
   1513        if (d == SkPathFirstDirection::kCCW) {
   1514            dst->privateReverseAddPath(src);
   1515        } else {
   1516            dst->addPath(src);
   1517        }
   1518    } else {
   1519        //  Seems like we can assume that a 2-point src would always result in
   1520        //  a convex stroke, but testing has proved otherwise.
   1521        //  TODO: fix the stroker to make this assumption true (without making
   1522        //  it slower that the work that will be done in computeConvexity())
   1523 #if 0
   1524        // this test results in a non-convex stroke :(
   1525        static void test(SkCanvas* canvas) {
   1526            SkPoint pts[] = { 146.333328,  192.333328, 300.333344, 293.333344 };
   1527            SkPaint paint;
   1528            paint.setStrokeWidth(7);
   1529            paint.setStrokeCap(SkPaint::kRound_Cap);
   1530            canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
   1531        }
   1532 #endif
   1533 #if 0
   1534        if (2 == src.countPoints()) {
   1535            dst->setIsConvex(true);
   1536        }
   1537 #endif
   1538    }
   1539 
   1540    // our answer should preserve the inverseness of the src
   1541    if (src.isInverseFillType()) {
   1542        SkASSERT(!dst->isInverseFillType());
   1543        dst->toggleInverseFillType();
   1544    }
   1545 }
   1546 
   1547 static SkPathDirection reverse_direction(SkPathDirection dir) {
   1548    static const SkPathDirection gOpposite[] = { SkPathDirection::kCCW, SkPathDirection::kCW };
   1549    return gOpposite[(int)dir];
   1550 }
   1551 
   1552 static void addBevel(SkPathBuilder* path, const SkRect& r, const SkRect& outer,
   1553                     SkPathDirection dir) {
   1554    SkPoint pts[8];
   1555 
   1556    if (SkPathDirection::kCW == dir) {
   1557        pts[0].set(r.fLeft, outer.fTop);
   1558        pts[1].set(r.fRight, outer.fTop);
   1559        pts[2].set(outer.fRight, r.fTop);
   1560        pts[3].set(outer.fRight, r.fBottom);
   1561        pts[4].set(r.fRight, outer.fBottom);
   1562        pts[5].set(r.fLeft, outer.fBottom);
   1563        pts[6].set(outer.fLeft, r.fBottom);
   1564        pts[7].set(outer.fLeft, r.fTop);
   1565    } else {
   1566        pts[7].set(r.fLeft, outer.fTop);
   1567        pts[6].set(r.fRight, outer.fTop);
   1568        pts[5].set(outer.fRight, r.fTop);
   1569        pts[4].set(outer.fRight, r.fBottom);
   1570        pts[3].set(r.fRight, outer.fBottom);
   1571        pts[2].set(r.fLeft, outer.fBottom);
   1572        pts[1].set(outer.fLeft, r.fBottom);
   1573        pts[0].set(outer.fLeft, r.fTop);
   1574    }
   1575    path->addPolygon(pts, true);
   1576 }
   1577 
   1578 void SkStroke::strokeRect(const SkRect& origRect, SkPathBuilder* dst,
   1579                          SkPathDirection dir) const {
   1580    SkASSERT(dst != nullptr);
   1581    dst->reset();
   1582 
   1583    SkScalar radius = SkScalarHalf(fWidth);
   1584    if (radius <= 0) {
   1585        return;
   1586    }
   1587 
   1588    SkScalar rw = origRect.width();
   1589    SkScalar rh = origRect.height();
   1590    if ((rw < 0) ^ (rh < 0)) {
   1591        dir = reverse_direction(dir);
   1592    }
   1593    SkRect rect(origRect);
   1594    rect.sort();
   1595    // reassign these, now that we know they'll be >= 0
   1596    rw = rect.width();
   1597    rh = rect.height();
   1598 
   1599    SkRect r(rect);
   1600    r.outset(radius, radius);
   1601 
   1602    SkPaint::Join join = (SkPaint::Join)fJoin;
   1603    if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
   1604        join = SkPaint::kBevel_Join;
   1605    }
   1606 
   1607    switch (join) {
   1608        case SkPaint::kMiter_Join:
   1609            dst->addRect(r, dir);
   1610            break;
   1611        case SkPaint::kBevel_Join:
   1612            addBevel(dst, rect, r, dir);
   1613            break;
   1614        case SkPaint::kRound_Join:
   1615            dst->addRRect(SkRRect::MakeRectXY(r, radius, radius), dir);
   1616            break;
   1617        default:
   1618            break;
   1619    }
   1620 
   1621    if (fWidth < std::min(rw, rh) && !fDoFill) {
   1622        r = rect;
   1623        r.inset(radius, radius);
   1624        dst->addRect(r, reverse_direction(dir));
   1625    }
   1626 }