LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkStroke.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 327 812 40.3 %
Date: 2017-07-14 16:53:18 Functions: 38 69 55.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13