LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkPath.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 574 1978 29.0 %
Date: 2017-07-14 16:53:18 Functions: 62 158 39.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2006 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 <cmath>
       9             : #include "SkBuffer.h"
      10             : #include "SkCubicClipper.h"
      11             : #include "SkGeometry.h"
      12             : #include "SkMath.h"
      13             : #include "SkPathPriv.h"
      14             : #include "SkPathRef.h"
      15             : #include "SkRRect.h"
      16             : 
      17           0 : static float poly_eval(float A, float B, float C, float t) {
      18           0 :     return (A * t + B) * t + C;
      19             : }
      20             : 
      21           0 : static float poly_eval(float A, float B, float C, float D, float t) {
      22           0 :     return ((A * t + B) * t + C) * t + D;
      23             : }
      24             : 
      25             : ////////////////////////////////////////////////////////////////////////////
      26             : 
      27             : /**
      28             :  *  Path.bounds is defined to be the bounds of all the control points.
      29             :  *  If we called bounds.join(r) we would skip r if r was empty, which breaks
      30             :  *  our promise. Hence we have a custom joiner that doesn't look at emptiness
      31             :  */
      32           0 : static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
      33           0 :     dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
      34           0 :     dst->fTop = SkMinScalar(dst->fTop, src.fTop);
      35           0 :     dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
      36           0 :     dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
      37           0 : }
      38             : 
      39         113 : static bool is_degenerate(const SkPath& path) {
      40         113 :     SkPath::Iter iter(path, false);
      41             :     SkPoint pts[4];
      42         113 :     return SkPath::kDone_Verb == iter.next(pts);
      43             : }
      44             : 
      45             : class SkAutoDisableDirectionCheck {
      46             : public:
      47         113 :     SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
      48         113 :         fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->fFirstDirection.load());
      49         113 :     }
      50             : 
      51         226 :     ~SkAutoDisableDirectionCheck() {
      52         113 :         fPath->fFirstDirection = fSaved;
      53         113 :     }
      54             : 
      55             : private:
      56             :     SkPath*                     fPath;
      57             :     SkPathPriv::FirstDirection  fSaved;
      58             : };
      59             : #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
      60             : 
      61             : /*  This guy's constructor/destructor bracket a path editing operation. It is
      62             :     used when we know the bounds of the amount we are going to add to the path
      63             :     (usually a new contour, but not required).
      64             : 
      65             :     It captures some state about the path up front (i.e. if it already has a
      66             :     cached bounds), and then if it can, it updates the cache bounds explicitly,
      67             :     avoiding the need to revisit all of the points in getBounds().
      68             : 
      69             :     It also notes if the path was originally degenerate, and if so, sets
      70             :     isConvex to true. Thus it can only be used if the contour being added is
      71             :     convex.
      72             :  */
      73             : class SkAutoPathBoundsUpdate {
      74             : public:
      75         113 :     SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
      76         113 :         this->init(path);
      77         113 :     }
      78             : 
      79             :     SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
      80             :                            SkScalar right, SkScalar bottom) {
      81             :         fRect.set(left, top, right, bottom);
      82             :         this->init(path);
      83             :     }
      84             : 
      85         226 :     ~SkAutoPathBoundsUpdate() {
      86         113 :         fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
      87         113 :                                         : SkPath::kUnknown_Convexity);
      88         113 :         if (fEmpty || fHasValidBounds) {
      89         113 :             fPath->setBounds(fRect);
      90             :         }
      91         113 :     }
      92             : 
      93             : private:
      94             :     SkPath* fPath;
      95             :     SkRect  fRect;
      96             :     bool    fHasValidBounds;
      97             :     bool    fDegenerate;
      98             :     bool    fEmpty;
      99             : 
     100         113 :     void init(SkPath* path) {
     101             :         // Cannot use fRect for our bounds unless we know it is sorted
     102         113 :         fRect.sort();
     103         113 :         fPath = path;
     104             :         // Mark the path's bounds as dirty if (1) they are, or (2) the path
     105             :         // is non-finite, and therefore its bounds are not meaningful
     106         113 :         fHasValidBounds = path->hasComputedBounds() && path->isFinite();
     107         113 :         fEmpty = path->isEmpty();
     108         113 :         if (fHasValidBounds && !fEmpty) {
     109           0 :             joinNoEmptyChecks(&fRect, fPath->getBounds());
     110             :         }
     111         113 :         fDegenerate = is_degenerate(*path);
     112         113 :     }
     113             : };
     114             : #define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
     115             : 
     116             : ////////////////////////////////////////////////////////////////////////////
     117             : 
     118             : /*
     119             :     Stores the verbs and points as they are given to us, with exceptions:
     120             :     - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
     121             :     - we insert a Move(0,0) if Line | Quad | Cubic is our first command
     122             : 
     123             :     The iterator does more cleanup, especially if forceClose == true
     124             :     1. If we encounter degenerate segments, remove them
     125             :     2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
     126             :     3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
     127             :     4. if we encounter Line | Quad | Cubic after Close, cons up a Move
     128             : */
     129             : 
     130             : ////////////////////////////////////////////////////////////////////////////
     131             : 
     132             : // flag to require a moveTo if we begin with something else, like lineTo etc.
     133             : #define INITIAL_LASTMOVETOINDEX_VALUE   ~0
     134             : 
     135         538 : SkPath::SkPath()
     136         538 :     : fPathRef(SkPathRef::CreateEmpty()) {
     137         538 :     this->resetFields();
     138         538 :     fIsVolatile = false;
     139         538 : }
     140             : 
     141         583 : void SkPath::resetFields() {
     142             :     //fPathRef is assumed to have been emptied by the caller.
     143         583 :     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
     144         583 :     fFillType = kWinding_FillType;
     145         583 :     fConvexity = kUnknown_Convexity;
     146         583 :     fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
     147             : 
     148             :     // We don't touch Android's fSourcePath.  It's used to track texture garbage collection, so we
     149             :     // don't want to muck with it if it's been set to something non-nullptr.
     150         583 : }
     151             : 
     152           0 : SkPath::SkPath(const SkPath& that)
     153           0 :     : fPathRef(SkRef(that.fPathRef.get())) {
     154           0 :     this->copyFields(that);
     155           0 :     SkDEBUGCODE(that.validate();)
     156           0 : }
     157             : 
     158         988 : SkPath::~SkPath() {
     159         494 :     SkDEBUGCODE(this->validate();)
     160         494 : }
     161             : 
     162          25 : SkPath& SkPath::operator=(const SkPath& that) {
     163          25 :     SkDEBUGCODE(that.validate();)
     164             : 
     165          25 :     if (this != &that) {
     166          25 :         fPathRef.reset(SkRef(that.fPathRef.get()));
     167          25 :         this->copyFields(that);
     168             :     }
     169          25 :     SkDEBUGCODE(this->validate();)
     170          25 :     return *this;
     171             : }
     172             : 
     173          25 : void SkPath::copyFields(const SkPath& that) {
     174             :     //fPathRef is assumed to have been set by the caller.
     175          25 :     fLastMoveToIndex = that.fLastMoveToIndex;
     176          25 :     fFillType        = that.fFillType;
     177          25 :     fConvexity       = that.fConvexity;
     178             :     // Simulate fFirstDirection  = that.fFirstDirection;
     179          25 :     fFirstDirection.store(that.fFirstDirection.load());
     180          25 :     fIsVolatile      = that.fIsVolatile;
     181          25 : }
     182             : 
     183           0 : bool operator==(const SkPath& a, const SkPath& b) {
     184             :     // note: don't need to look at isConvex or bounds, since just comparing the
     185             :     // raw data is sufficient.
     186           0 :     return &a == &b ||
     187           0 :         (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
     188             : }
     189             : 
     190         127 : void SkPath::swap(SkPath& that) {
     191         127 :     if (this != &that) {
     192         127 :         fPathRef.swap(that.fPathRef);
     193         127 :         SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
     194         127 :         SkTSwap<uint8_t>(fFillType, that.fFillType);
     195         127 :         SkTSwap<uint8_t>(fConvexity, that.fConvexity);
     196             :         // Simulate SkTSwap<uint8_t>(fFirstDirection, that.fFirstDirection);
     197         127 :         uint8_t temp = fFirstDirection;
     198         127 :         fFirstDirection.store(that.fFirstDirection.load());
     199         127 :         that.fFirstDirection.store(temp);
     200         127 :         SkTSwap<SkBool8>(fIsVolatile, that.fIsVolatile);
     201             :     }
     202         127 : }
     203             : 
     204           0 : bool SkPath::isInterpolatable(const SkPath& compare) const {
     205           0 :     int count = fPathRef->countVerbs();
     206           0 :     if (count != compare.fPathRef->countVerbs()) {
     207           0 :         return false;
     208             :     }
     209           0 :     if (!count) {
     210           0 :         return true;
     211             :     }
     212           0 :     if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
     213             :                count)) {
     214           0 :         return false;
     215             :     }
     216           0 :     return !fPathRef->countWeights() ||
     217           0 :             !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
     218             :             fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
     219             : }
     220             : 
     221           0 : bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
     222           0 :     int verbCount = fPathRef->countVerbs();
     223           0 :     if (verbCount != ending.fPathRef->countVerbs()) {
     224           0 :         return false;
     225             :     }
     226           0 :     if (!verbCount) {
     227           0 :         return true;
     228             :     }
     229           0 :     out->reset();
     230           0 :     out->addPath(*this);
     231           0 :     fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get());
     232           0 :     return true;
     233             : }
     234             : 
     235           0 : static inline bool check_edge_against_rect(const SkPoint& p0,
     236             :                                            const SkPoint& p1,
     237             :                                            const SkRect& rect,
     238             :                                            SkPathPriv::FirstDirection dir) {
     239             :     const SkPoint* edgeBegin;
     240             :     SkVector v;
     241           0 :     if (SkPathPriv::kCW_FirstDirection == dir) {
     242           0 :         v = p1 - p0;
     243           0 :         edgeBegin = &p0;
     244             :     } else {
     245           0 :         v = p0 - p1;
     246           0 :         edgeBegin = &p1;
     247             :     }
     248           0 :     if (v.fX || v.fY) {
     249             :         // check the cross product of v with the vec from edgeBegin to each rect corner
     250           0 :         SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX);
     251           0 :         SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY);
     252           0 :         SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX);
     253           0 :         SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY);
     254           0 :         if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
     255           0 :             return false;
     256             :         }
     257             :     }
     258           0 :     return true;
     259             : }
     260             : 
     261           0 : bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
     262             :     // This only handles non-degenerate convex paths currently.
     263           0 :     if (kConvex_Convexity != this->getConvexity()) {
     264           0 :         return false;
     265             :     }
     266             : 
     267             :     SkPathPriv::FirstDirection direction;
     268           0 :     if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
     269           0 :         return false;
     270             :     }
     271             : 
     272             :     SkPoint firstPt;
     273             :     SkPoint prevPt;
     274           0 :     SkPath::Iter iter(*this, true);
     275             :     SkPath::Verb verb;
     276             :     SkPoint pts[4];
     277           0 :     SkDEBUGCODE(int moveCnt = 0;)
     278           0 :     SkDEBUGCODE(int segmentCount = 0;)
     279           0 :     SkDEBUGCODE(int closeCount = 0;)
     280             : 
     281           0 :     while ((verb = iter.next(pts, true, true)) != kDone_Verb) {
     282           0 :         int nextPt = -1;
     283           0 :         switch (verb) {
     284             :             case kMove_Verb:
     285           0 :                 SkASSERT(!segmentCount && !closeCount);
     286           0 :                 SkDEBUGCODE(++moveCnt);
     287           0 :                 firstPt = prevPt = pts[0];
     288           0 :                 break;
     289             :             case kLine_Verb:
     290           0 :                 nextPt = 1;
     291           0 :                 SkASSERT(moveCnt && !closeCount);
     292           0 :                 SkDEBUGCODE(++segmentCount);
     293           0 :                 break;
     294             :             case kQuad_Verb:
     295             :             case kConic_Verb:
     296           0 :                 SkASSERT(moveCnt && !closeCount);
     297           0 :                 SkDEBUGCODE(++segmentCount);
     298           0 :                 nextPt = 2;
     299           0 :                 break;
     300             :             case kCubic_Verb:
     301           0 :                 SkASSERT(moveCnt && !closeCount);
     302           0 :                 SkDEBUGCODE(++segmentCount);
     303           0 :                 nextPt = 3;
     304           0 :                 break;
     305             :             case kClose_Verb:
     306           0 :                 SkDEBUGCODE(++closeCount;)
     307           0 :                 break;
     308             :             default:
     309           0 :                 SkDEBUGFAIL("unknown verb");
     310             :         }
     311           0 :         if (-1 != nextPt) {
     312           0 :             if (SkPath::kConic_Verb == verb) {
     313           0 :                 SkConic orig;
     314           0 :                 orig.set(pts, iter.conicWeight());
     315             :                 SkPoint quadPts[5];
     316           0 :                 int count = orig.chopIntoQuadsPOW2(quadPts, 1);
     317           0 :                 SkASSERT_RELEASE(2 == count);
     318             : 
     319           0 :                 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
     320           0 :                     return false;
     321             :                 }
     322           0 :                 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
     323           0 :                     return false;
     324             :                 }
     325             :             } else {
     326           0 :                 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
     327           0 :                     return false;
     328             :                 }
     329             :             }
     330           0 :             prevPt = pts[nextPt];
     331             :         }
     332             :     }
     333             : 
     334           0 :     return check_edge_against_rect(prevPt, firstPt, rect, direction);
     335             : }
     336             : 
     337           0 : uint32_t SkPath::getGenerationID() const {
     338           0 :     uint32_t genID = fPathRef->genID();
     339             : #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     340             :     SkASSERT((unsigned)fFillType < (1 << (32 - kPathRefGenIDBitCnt)));
     341             :     genID |= static_cast<uint32_t>(fFillType) << kPathRefGenIDBitCnt;
     342             : #endif
     343           0 :     return genID;
     344             : }
     345             : 
     346          15 : void SkPath::reset() {
     347          15 :     SkDEBUGCODE(this->validate();)
     348             : 
     349          15 :     fPathRef.reset(SkPathRef::CreateEmpty());
     350          15 :     this->resetFields();
     351          15 : }
     352             : 
     353          30 : void SkPath::rewind() {
     354          30 :     SkDEBUGCODE(this->validate();)
     355             : 
     356          30 :     SkPathRef::Rewind(&fPathRef);
     357          30 :     this->resetFields();
     358          30 : }
     359             : 
     360           0 : bool SkPath::isLastContourClosed() const {
     361           0 :     int verbCount = fPathRef->countVerbs();
     362           0 :     if (0 == verbCount) {
     363           0 :         return false;
     364             :     }
     365           0 :     return kClose_Verb == fPathRef->atVerb(verbCount - 1);
     366             : }
     367             : 
     368           0 : bool SkPath::isLine(SkPoint line[2]) const {
     369           0 :     int verbCount = fPathRef->countVerbs();
     370             : 
     371           0 :     if (2 == verbCount) {
     372           0 :         SkASSERT(kMove_Verb == fPathRef->atVerb(0));
     373           0 :         if (kLine_Verb == fPathRef->atVerb(1)) {
     374           0 :             SkASSERT(2 == fPathRef->countPoints());
     375           0 :             if (line) {
     376           0 :                 const SkPoint* pts = fPathRef->points();
     377           0 :                 line[0] = pts[0];
     378           0 :                 line[1] = pts[1];
     379             :             }
     380           0 :             return true;
     381             :         }
     382             :     }
     383           0 :     return false;
     384             : }
     385             : 
     386             : /*
     387             :  Determines if path is a rect by keeping track of changes in direction
     388             :  and looking for a loop either clockwise or counterclockwise.
     389             : 
     390             :  The direction is computed such that:
     391             :   0: vertical up
     392             :   1: horizontal left
     393             :   2: vertical down
     394             :   3: horizontal right
     395             : 
     396             : A rectangle cycles up/right/down/left or up/left/down/right.
     397             : 
     398             : The test fails if:
     399             :   The path is closed, and followed by a line.
     400             :   A second move creates a new endpoint.
     401             :   A diagonal line is parsed.
     402             :   There's more than four changes of direction.
     403             :   There's a discontinuity on the line (e.g., a move in the middle)
     404             :   The line reverses direction.
     405             :   The path contains a quadratic or cubic.
     406             :   The path contains fewer than four points.
     407             :  *The rectangle doesn't complete a cycle.
     408             :  *The final point isn't equal to the first point.
     409             : 
     410             :   *These last two conditions we relax if we have a 3-edge path that would
     411             :    form a rectangle if it were closed (as we do when we fill a path)
     412             : 
     413             : It's OK if the path has:
     414             :   Several colinear line segments composing a rectangle side.
     415             :   Single points on the rectangle side.
     416             : 
     417             : The direction takes advantage of the corners found since opposite sides
     418             : must travel in opposite directions.
     419             : 
     420             : FIXME: Allow colinear quads and cubics to be treated like lines.
     421             : FIXME: If the API passes fill-only, return true if the filled stroke
     422             :        is a rectangle, though the caller failed to close the path.
     423             : 
     424             :  first,last,next direction state-machine:
     425             :     0x1 is set if the segment is horizontal
     426             :     0x2 is set if the segment is moving to the right or down
     427             :  thus:
     428             :     two directions are opposites iff (dirA ^ dirB) == 0x2
     429             :     two directions are perpendicular iff (dirA ^ dirB) == 0x1
     430             : 
     431             :  */
     432         109 : static int rect_make_dir(SkScalar dx, SkScalar dy) {
     433         109 :     return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
     434             : }
     435          54 : bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
     436             :         bool* isClosed, Direction* direction) const {
     437          54 :     int corners = 0;
     438             :     SkPoint first, last;
     439          54 :     const SkPoint* pts = *ptsPtr;
     440          54 :     const SkPoint* savePts = nullptr;
     441          54 :     first.set(0, 0);
     442          54 :     last.set(0, 0);
     443          54 :     int firstDirection = 0;
     444          54 :     int lastDirection = 0;
     445          54 :     int nextDirection = 0;
     446          54 :     bool closedOrMoved = false;
     447          54 :     bool autoClose = false;
     448          54 :     bool insertClose = false;
     449          54 :     int verbCnt = fPathRef->countVerbs();
     450         378 :     while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
     451         203 :         uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
     452         203 :         switch (verb) {
     453             :             case kClose_Verb:
     454          16 :                 savePts = pts;
     455          16 :                 pts = *ptsPtr;
     456          16 :                 autoClose = true;
     457          16 :                 insertClose = false;
     458             :             case kLine_Verb: {
     459         108 :                 SkScalar left = last.fX;
     460         108 :                 SkScalar top = last.fY;
     461         108 :                 SkScalar right = pts->fX;
     462         108 :                 SkScalar bottom = pts->fY;
     463         108 :                 ++pts;
     464         108 :                 if (left != right && top != bottom) {
     465           0 :                     return false; // diagonal
     466             :                 }
     467         108 :                 if (left == right && top == bottom) {
     468          12 :                     break; // single point on side OK
     469             :                 }
     470          96 :                 nextDirection = rect_make_dir(right - left, bottom - top);
     471          96 :                 if (0 == corners) {
     472          42 :                     firstDirection = nextDirection;
     473          42 :                     first = last;
     474          42 :                     last = pts[-1];
     475          42 :                     corners = 1;
     476          42 :                     closedOrMoved = false;
     477          42 :                     break;
     478             :                 }
     479          54 :                 if (closedOrMoved) {
     480           0 :                     return false; // closed followed by a line
     481             :                 }
     482          54 :                 if (autoClose && nextDirection == firstDirection) {
     483           0 :                     break; // colinear with first
     484             :                 }
     485          54 :                 closedOrMoved = autoClose;
     486          54 :                 if (lastDirection != nextDirection) {
     487          54 :                     if (++corners > 4) {
     488           0 :                         return false; // too many direction changes
     489             :                     }
     490             :                 }
     491          54 :                 last = pts[-1];
     492          54 :                 if (lastDirection == nextDirection) {
     493           0 :                     break; // colinear segment
     494             :                 }
     495             :                 // Possible values for corners are 2, 3, and 4.
     496             :                 // When corners == 3, nextDirection opposes firstDirection.
     497             :                 // Otherwise, nextDirection at corner 2 opposes corner 4.
     498          54 :                 int turn = firstDirection ^ (corners - 1);
     499          54 :                 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
     500          54 :                 if ((directionCycle ^ turn) != nextDirection) {
     501           0 :                     return false; // direction didn't follow cycle
     502             :                 }
     503          54 :                 break;
     504             :             }
     505             :             case kQuad_Verb:
     506             :             case kConic_Verb:
     507             :             case kCubic_Verb:
     508          25 :                 return false; // quadratic, cubic not allowed
     509             :             case kMove_Verb:
     510          70 :                 if (allowPartial && !autoClose && firstDirection) {
     511           0 :                     insertClose = true;
     512           0 :                     *currVerb -= 1;  // try move again afterwards
     513           0 :                     goto addMissingClose;
     514             :                 }
     515          70 :                 if (pts != *ptsPtr) {
     516          16 :                     return false;
     517             :                 }
     518          54 :                 last = *pts++;
     519          54 :                 closedOrMoved = true;
     520          54 :                 break;
     521             :             default:
     522           0 :                 SkDEBUGFAIL("unexpected verb");
     523           0 :                 break;
     524             :         }
     525         162 :         *currVerb += 1;
     526         162 :         lastDirection = nextDirection;
     527             : addMissingClose:
     528             :         ;
     529             :     }
     530             :     // Success if 4 corners and first point equals last
     531          13 :     bool result = 4 == corners && (first == last || autoClose);
     532          13 :     if (!result) {
     533             :         // check if we are just an incomplete rectangle, in which case we can
     534             :         // return true, but not claim to be closed.
     535             :         // e.g.
     536             :         //    3 sided rectangle
     537             :         //    4 sided but the last edge is not long enough to reach the start
     538             :         //
     539          13 :         SkScalar closeX = first.x() - last.x();
     540          13 :         SkScalar closeY = first.y() - last.y();
     541          13 :         if (closeX && closeY) {
     542           0 :             return false;   // we're diagonal, abort (can we ever reach this?)
     543             :         }
     544          13 :         int closeDirection = rect_make_dir(closeX, closeY);
     545             :         // make sure the close-segment doesn't double-back on itself
     546          13 :         if (3 == corners || (4 == corners && closeDirection == lastDirection)) {
     547           0 :             result = true;
     548           0 :             autoClose = false;  // we are not closed
     549             :         }
     550             :     }
     551          13 :     if (savePts) {
     552           0 :         *ptsPtr = savePts;
     553             :     }
     554          13 :     if (result && isClosed) {
     555           0 :         *isClosed = autoClose;
     556             :     }
     557          13 :     if (result && direction) {
     558           0 :         *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
     559             :     }
     560          13 :     return result;
     561             : }
     562             : 
     563          54 : bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
     564          54 :     SkDEBUGCODE(this->validate();)
     565          54 :     int currVerb = 0;
     566          54 :     const SkPoint* pts = fPathRef->points();
     567          54 :     const SkPoint* first = pts;
     568          54 :     if (!this->isRectContour(false, &currVerb, &pts, isClosed, direction)) {
     569          54 :         return false;
     570             :     }
     571           0 :     if (rect) {
     572           0 :         int32_t num = SkToS32(pts - first);
     573           0 :         if (num) {
     574           0 :             rect->set(first, num);
     575             :         } else {
     576             :             // 'pts' isn't updated for open rects
     577           0 :             *rect = this->getBounds();
     578             :         }
     579             :     }
     580           0 :     return true;
     581             : }
     582             : 
     583           0 : bool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
     584           0 :     SkDEBUGCODE(this->validate();)
     585           0 :     int currVerb = 0;
     586           0 :     const SkPoint* pts = fPathRef->points();
     587           0 :     const SkPoint* first = pts;
     588             :     Direction testDirs[2];
     589           0 :     if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0])) {
     590           0 :         return false;
     591             :     }
     592           0 :     const SkPoint* last = pts;
     593             :     SkRect testRects[2];
     594             :     bool isClosed;
     595           0 :     if (isRectContour(false, &currVerb, &pts, &isClosed, &testDirs[1])) {
     596           0 :         testRects[0].set(first, SkToS32(last - first));
     597           0 :         if (!isClosed) {
     598           0 :             pts = fPathRef->points() + fPathRef->countPoints();
     599             :         }
     600           0 :         testRects[1].set(last, SkToS32(pts - last));
     601           0 :         if (testRects[0].contains(testRects[1])) {
     602           0 :             if (rects) {
     603           0 :                 rects[0] = testRects[0];
     604           0 :                 rects[1] = testRects[1];
     605             :             }
     606           0 :             if (dirs) {
     607           0 :                 dirs[0] = testDirs[0];
     608           0 :                 dirs[1] = testDirs[1];
     609             :             }
     610           0 :             return true;
     611             :         }
     612           0 :         if (testRects[1].contains(testRects[0])) {
     613           0 :             if (rects) {
     614           0 :                 rects[0] = testRects[1];
     615           0 :                 rects[1] = testRects[0];
     616             :             }
     617           0 :             if (dirs) {
     618           0 :                 dirs[0] = testDirs[1];
     619           0 :                 dirs[1] = testDirs[0];
     620             :             }
     621           0 :             return true;
     622             :         }
     623             :     }
     624           0 :     return false;
     625             : }
     626             : 
     627        1829 : int SkPath::countPoints() const {
     628        1829 :     return fPathRef->countPoints();
     629             : }
     630             : 
     631           0 : int SkPath::getPoints(SkPoint dst[], int max) const {
     632           0 :     SkDEBUGCODE(this->validate();)
     633             : 
     634           0 :     SkASSERT(max >= 0);
     635           0 :     SkASSERT(!max || dst);
     636           0 :     int count = SkMin32(max, fPathRef->countPoints());
     637           0 :     sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
     638           0 :     return fPathRef->countPoints();
     639             : }
     640             : 
     641          42 : SkPoint SkPath::getPoint(int index) const {
     642          42 :     if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
     643          42 :         return fPathRef->atPoint(index);
     644             :     }
     645           0 :     return SkPoint::Make(0, 0);
     646             : }
     647             : 
     648         339 : int SkPath::countVerbs() const {
     649         339 :     return fPathRef->countVerbs();
     650             : }
     651             : 
     652           0 : static inline void copy_verbs_reverse(uint8_t* inorderDst,
     653             :                                       const uint8_t* reversedSrc,
     654             :                                       int count) {
     655           0 :     for (int i = 0; i < count; ++i) {
     656           0 :         inorderDst[i] = reversedSrc[~i];
     657             :     }
     658           0 : }
     659             : 
     660           0 : int SkPath::getVerbs(uint8_t dst[], int max) const {
     661           0 :     SkDEBUGCODE(this->validate();)
     662             : 
     663           0 :     SkASSERT(max >= 0);
     664           0 :     SkASSERT(!max || dst);
     665           0 :     int count = SkMin32(max, fPathRef->countVerbs());
     666           0 :     copy_verbs_reverse(dst, fPathRef->verbs(), count);
     667           0 :     return fPathRef->countVerbs();
     668             : }
     669             : 
     670          15 : bool SkPath::getLastPt(SkPoint* lastPt) const {
     671          15 :     SkDEBUGCODE(this->validate();)
     672             : 
     673          15 :     int count = fPathRef->countPoints();
     674          15 :     if (count > 0) {
     675          15 :         if (lastPt) {
     676          15 :             *lastPt = fPathRef->atPoint(count - 1);
     677             :         }
     678          15 :         return true;
     679             :     }
     680           0 :     if (lastPt) {
     681           0 :         lastPt->set(0, 0);
     682             :     }
     683           0 :     return false;
     684             : }
     685             : 
     686           0 : void SkPath::setPt(int index, SkScalar x, SkScalar y) {
     687           0 :     SkDEBUGCODE(this->validate();)
     688             : 
     689           0 :     int count = fPathRef->countPoints();
     690           0 :     if (count <= index) {
     691           0 :         return;
     692             :     } else {
     693           0 :         SkPathRef::Editor ed(&fPathRef);
     694           0 :         ed.atPoint(index)->set(x, y);
     695             :     }
     696             : }
     697             : 
     698           0 : void SkPath::setLastPt(SkScalar x, SkScalar y) {
     699           0 :     SkDEBUGCODE(this->validate();)
     700             : 
     701           0 :     int count = fPathRef->countPoints();
     702           0 :     if (count == 0) {
     703           0 :         this->moveTo(x, y);
     704             :     } else {
     705           0 :         SkPathRef::Editor ed(&fPathRef);
     706           0 :         ed.atPoint(count-1)->set(x, y);
     707             :     }
     708           0 : }
     709             : 
     710         113 : void SkPath::setConvexity(Convexity c) {
     711         113 :     if (fConvexity != c) {
     712         113 :         fConvexity = c;
     713             :     }
     714         113 : }
     715             : 
     716             : //////////////////////////////////////////////////////////////////////////////
     717             : //  Construction methods
     718             : 
     719             : #define DIRTY_AFTER_EDIT                                        \
     720             :     do {                                                        \
     721             :         fConvexity = kUnknown_Convexity;                        \
     722             :         fFirstDirection = SkPathPriv::kUnknown_FirstDirection;  \
     723             :     } while (0)
     724             : 
     725         143 : void SkPath::incReserve(U16CPU inc) {
     726         143 :     SkDEBUGCODE(this->validate();)
     727         143 :     SkPathRef::Editor(&fPathRef, inc, inc);
     728         143 :     SkDEBUGCODE(this->validate();)
     729         143 : }
     730             : 
     731         375 : void SkPath::moveTo(SkScalar x, SkScalar y) {
     732         375 :     SkDEBUGCODE(this->validate();)
     733             : 
     734         750 :     SkPathRef::Editor ed(&fPathRef);
     735             : 
     736             :     // remember our index
     737         375 :     fLastMoveToIndex = fPathRef->countPoints();
     738             : 
     739         375 :     ed.growForVerb(kMove_Verb)->set(x, y);
     740             : 
     741         375 :     DIRTY_AFTER_EDIT;
     742         375 : }
     743             : 
     744           0 : void SkPath::rMoveTo(SkScalar x, SkScalar y) {
     745             :     SkPoint pt;
     746           0 :     this->getLastPt(&pt);
     747           0 :     this->moveTo(pt.fX + x, pt.fY + y);
     748           0 : }
     749             : 
     750        1983 : void SkPath::injectMoveToIfNeeded() {
     751        1983 :     if (fLastMoveToIndex < 0) {
     752             :         SkScalar x, y;
     753           0 :         if (fPathRef->countVerbs() == 0) {
     754           0 :             x = y = 0;
     755             :         } else {
     756           0 :             const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
     757           0 :             x = pt.fX;
     758           0 :             y = pt.fY;
     759             :         }
     760           0 :         this->moveTo(x, y);
     761             :     }
     762        1983 : }
     763             : 
     764        1311 : void SkPath::lineTo(SkScalar x, SkScalar y) {
     765        1311 :     SkDEBUGCODE(this->validate();)
     766             : 
     767        1311 :     this->injectMoveToIfNeeded();
     768             : 
     769        2622 :     SkPathRef::Editor ed(&fPathRef);
     770        1311 :     ed.growForVerb(kLine_Verb)->set(x, y);
     771             : 
     772        1311 :     DIRTY_AFTER_EDIT;
     773        1311 : }
     774             : 
     775           0 : void SkPath::rLineTo(SkScalar x, SkScalar y) {
     776           0 :     this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
     777             :     SkPoint pt;
     778           0 :     this->getLastPt(&pt);
     779           0 :     this->lineTo(pt.fX + x, pt.fY + y);
     780           0 : }
     781             : 
     782          24 : void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     783          24 :     SkDEBUGCODE(this->validate();)
     784             : 
     785          24 :     this->injectMoveToIfNeeded();
     786             : 
     787          48 :     SkPathRef::Editor ed(&fPathRef);
     788          24 :     SkPoint* pts = ed.growForVerb(kQuad_Verb);
     789          24 :     pts[0].set(x1, y1);
     790          24 :     pts[1].set(x2, y2);
     791             : 
     792          24 :     DIRTY_AFTER_EDIT;
     793          24 : }
     794             : 
     795           0 : void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     796           0 :     this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
     797             :     SkPoint pt;
     798           0 :     this->getLastPt(&pt);
     799           0 :     this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
     800           0 : }
     801             : 
     802           0 : void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     803             :                      SkScalar w) {
     804             :     // check for <= 0 or NaN with this test
     805           0 :     if (!(w > 0)) {
     806           0 :         this->lineTo(x2, y2);
     807           0 :     } else if (!SkScalarIsFinite(w)) {
     808           0 :         this->lineTo(x1, y1);
     809           0 :         this->lineTo(x2, y2);
     810           0 :     } else if (SK_Scalar1 == w) {
     811           0 :         this->quadTo(x1, y1, x2, y2);
     812             :     } else {
     813           0 :         SkDEBUGCODE(this->validate();)
     814             : 
     815           0 :         this->injectMoveToIfNeeded();
     816             : 
     817           0 :         SkPathRef::Editor ed(&fPathRef);
     818           0 :         SkPoint* pts = ed.growForVerb(kConic_Verb, w);
     819           0 :         pts[0].set(x1, y1);
     820           0 :         pts[1].set(x2, y2);
     821             : 
     822           0 :         DIRTY_AFTER_EDIT;
     823             :     }
     824           0 : }
     825             : 
     826           0 : void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
     827             :                       SkScalar w) {
     828           0 :     this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
     829             :     SkPoint pt;
     830           0 :     this->getLastPt(&pt);
     831           0 :     this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
     832           0 : }
     833             : 
     834         648 : void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     835             :                      SkScalar x3, SkScalar y3) {
     836         648 :     SkDEBUGCODE(this->validate();)
     837             : 
     838         648 :     this->injectMoveToIfNeeded();
     839             : 
     840        1296 :     SkPathRef::Editor ed(&fPathRef);
     841         648 :     SkPoint* pts = ed.growForVerb(kCubic_Verb);
     842         648 :     pts[0].set(x1, y1);
     843         648 :     pts[1].set(x2, y2);
     844         648 :     pts[2].set(x3, y3);
     845             : 
     846         648 :     DIRTY_AFTER_EDIT;
     847         648 : }
     848             : 
     849           0 : void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     850             :                       SkScalar x3, SkScalar y3) {
     851           0 :     this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
     852             :     SkPoint pt;
     853           0 :     this->getLastPt(&pt);
     854           0 :     this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
     855           0 :                   pt.fX + x3, pt.fY + y3);
     856           0 : }
     857             : 
     858         346 : void SkPath::close() {
     859         346 :     SkDEBUGCODE(this->validate();)
     860             : 
     861         346 :     int count = fPathRef->countVerbs();
     862         346 :     if (count > 0) {
     863         346 :         switch (fPathRef->atVerb(count - 1)) {
     864             :             case kLine_Verb:
     865             :             case kQuad_Verb:
     866             :             case kConic_Verb:
     867             :             case kCubic_Verb:
     868             :             case kMove_Verb: {
     869         692 :                 SkPathRef::Editor ed(&fPathRef);
     870         346 :                 ed.growForVerb(kClose_Verb);
     871         346 :                 break;
     872             :             }
     873             :             case kClose_Verb:
     874             :                 // don't add a close if it's the first verb or a repeat
     875           0 :                 break;
     876             :             default:
     877           0 :                 SkDEBUGFAIL("unexpected verb");
     878           0 :                 break;
     879             :         }
     880             :     }
     881             : 
     882             :     // signal that we need a moveTo to follow us (unless we're done)
     883             : #if 0
     884             :     if (fLastMoveToIndex >= 0) {
     885             :         fLastMoveToIndex = ~fLastMoveToIndex;
     886             :     }
     887             : #else
     888         346 :     fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
     889             : #endif
     890         346 : }
     891             : 
     892             : ///////////////////////////////////////////////////////////////////////////////
     893             : 
     894             : namespace {
     895             : 
     896             : template <unsigned N>
     897             : class PointIterator {
     898             : public:
     899         113 :     PointIterator(SkPath::Direction dir, unsigned startIndex)
     900         113 :         : fCurrent(startIndex % N)
     901         113 :         , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
     902             : 
     903         452 :     const SkPoint& current() const {
     904         452 :         SkASSERT(fCurrent < N);
     905         452 :         return fPts[fCurrent];
     906             :     }
     907             : 
     908         339 :     const SkPoint& next() {
     909         339 :         fCurrent = (fCurrent + fAdvance) % N;
     910         339 :         return this->current();
     911             :     }
     912             : 
     913             : protected:
     914             :     SkPoint fPts[N];
     915             : 
     916             : private:
     917             :     unsigned fCurrent;
     918             :     unsigned fAdvance;
     919             : };
     920             : 
     921             : class RectPointIterator : public PointIterator<4> {
     922             : public:
     923         113 :     RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
     924         113 :         : PointIterator(dir, startIndex) {
     925             : 
     926         113 :         fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
     927         113 :         fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
     928         113 :         fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
     929         113 :         fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
     930         113 :     }
     931             : };
     932             : 
     933             : class OvalPointIterator : public PointIterator<4> {
     934             : public:
     935           0 :     OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
     936           0 :         : PointIterator(dir, startIndex) {
     937             : 
     938           0 :         const SkScalar cx = oval.centerX();
     939           0 :         const SkScalar cy = oval.centerY();
     940             : 
     941           0 :         fPts[0] = SkPoint::Make(cx, oval.fTop);
     942           0 :         fPts[1] = SkPoint::Make(oval.fRight, cy);
     943           0 :         fPts[2] = SkPoint::Make(cx, oval.fBottom);
     944           0 :         fPts[3] = SkPoint::Make(oval.fLeft, cy);
     945           0 :     }
     946             : };
     947             : 
     948             : class RRectPointIterator : public PointIterator<8> {
     949             : public:
     950           0 :     RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
     951           0 :         : PointIterator(dir, startIndex) {
     952             : 
     953           0 :         const SkRect& bounds = rrect.getBounds();
     954           0 :         const SkScalar L = bounds.fLeft;
     955           0 :         const SkScalar T = bounds.fTop;
     956           0 :         const SkScalar R = bounds.fRight;
     957           0 :         const SkScalar B = bounds.fBottom;
     958             : 
     959           0 :         fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
     960           0 :         fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
     961           0 :         fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
     962           0 :         fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
     963           0 :         fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
     964           0 :         fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
     965           0 :         fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
     966           0 :         fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
     967           0 :     }
     968             : };
     969             : 
     970             : } // anonymous namespace
     971             : 
     972         113 : static void assert_known_direction(int dir) {
     973         113 :     SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
     974         113 : }
     975             : 
     976         113 : void SkPath::addRect(const SkRect& rect, Direction dir) {
     977         113 :     this->addRect(rect, dir, 0);
     978         113 : }
     979             : 
     980           0 : void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
     981             :                      SkScalar bottom, Direction dir) {
     982           0 :     this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
     983           0 : }
     984             : 
     985         113 : void SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
     986         113 :     assert_known_direction(dir);
     987         226 :     fFirstDirection = this->hasOnlyMoveTos() ?
     988         113 :         (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
     989         226 :     SkAutoDisableDirectionCheck addc(this);
     990         226 :     SkAutoPathBoundsUpdate apbu(this, rect);
     991             : 
     992         113 :     SkDEBUGCODE(int initialVerbCount = this->countVerbs());
     993             : 
     994         113 :     const int kVerbs = 5; // moveTo + 3x lineTo + close
     995         113 :     this->incReserve(kVerbs);
     996             : 
     997         113 :     RectPointIterator iter(rect, dir, startIndex);
     998             : 
     999         113 :     this->moveTo(iter.current());
    1000         113 :     this->lineTo(iter.next());
    1001         113 :     this->lineTo(iter.next());
    1002         113 :     this->lineTo(iter.next());
    1003         113 :     this->close();
    1004             : 
    1005         113 :     SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
    1006         113 : }
    1007             : 
    1008           0 : void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
    1009           0 :     SkDEBUGCODE(this->validate();)
    1010           0 :     if (count <= 0) {
    1011           0 :         return;
    1012             :     }
    1013             : 
    1014           0 :     fLastMoveToIndex = fPathRef->countPoints();
    1015             : 
    1016             :     // +close makes room for the extra kClose_Verb
    1017           0 :     SkPathRef::Editor ed(&fPathRef, count+close, count);
    1018             : 
    1019           0 :     ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
    1020           0 :     if (count > 1) {
    1021           0 :         SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
    1022           0 :         memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
    1023             :     }
    1024             : 
    1025           0 :     if (close) {
    1026           0 :         ed.growForVerb(kClose_Verb);
    1027           0 :         fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
    1028             :     }
    1029             : 
    1030           0 :     DIRTY_AFTER_EDIT;
    1031           0 :     SkDEBUGCODE(this->validate();)
    1032             : }
    1033             : 
    1034             : #include "SkGeometry.h"
    1035             : 
    1036           0 : static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
    1037             :                               SkPoint* pt) {
    1038           0 :     if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
    1039             :         // Chrome uses this path to move into and out of ovals. If not
    1040             :         // treated as a special case the moves can distort the oval's
    1041             :         // bounding box (and break the circle special case).
    1042           0 :         pt->set(oval.fRight, oval.centerY());
    1043           0 :         return true;
    1044           0 :     } else if (0 == oval.width() && 0 == oval.height()) {
    1045             :         // Chrome will sometimes create 0 radius round rects. Having degenerate
    1046             :         // quad segments in the path prevents the path from being recognized as
    1047             :         // a rect.
    1048             :         // TODO: optimizing the case where only one of width or height is zero
    1049             :         // should also be considered. This case, however, doesn't seem to be
    1050             :         // as common as the single point case.
    1051           0 :         pt->set(oval.fRight, oval.fTop);
    1052           0 :         return true;
    1053             :     }
    1054           0 :     return false;
    1055             : }
    1056             : 
    1057             : // Return the unit vectors pointing at the start/stop points for the given start/sweep angles
    1058             : //
    1059           0 : static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
    1060             :                                    SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
    1061           0 :     startV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &startV->fX);
    1062           0 :     stopV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), &stopV->fX);
    1063             : 
    1064             :     /*  If the sweep angle is nearly (but less than) 360, then due to precision
    1065             :      loss in radians-conversion and/or sin/cos, we may end up with coincident
    1066             :      vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
    1067             :      of drawing a nearly complete circle (good).
    1068             :      e.g. canvas.drawArc(0, 359.99, ...)
    1069             :      -vs- canvas.drawArc(0, 359.9, ...)
    1070             :      We try to detect this edge case, and tweak the stop vector
    1071             :      */
    1072           0 :     if (*startV == *stopV) {
    1073           0 :         SkScalar sw = SkScalarAbs(sweepAngle);
    1074           0 :         if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
    1075           0 :             SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
    1076             :             // make a guess at a tiny angle (in radians) to tweak by
    1077           0 :             SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
    1078             :             // not sure how much will be enough, so we use a loop
    1079           0 :             do {
    1080           0 :                 stopRad -= deltaRad;
    1081           0 :                 stopV->fY = SkScalarSinCos(stopRad, &stopV->fX);
    1082             :             } while (*startV == *stopV);
    1083             :         }
    1084             :     }
    1085           0 :     *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
    1086           0 : }
    1087             : 
    1088             : /**
    1089             :  *  If this returns 0, then the caller should just line-to the singlePt, else it should
    1090             :  *  ignore singlePt and append the specified number of conics.
    1091             :  */
    1092           0 : static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
    1093             :                             SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
    1094             :                             SkPoint* singlePt) {
    1095             :     SkMatrix    matrix;
    1096             : 
    1097           0 :     matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
    1098           0 :     matrix.postTranslate(oval.centerX(), oval.centerY());
    1099             : 
    1100           0 :     int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
    1101           0 :     if (0 == count) {
    1102           0 :         matrix.mapXY(stop.x(), stop.y(), singlePt);
    1103             :     }
    1104           0 :     return count;
    1105             : }
    1106             : 
    1107           0 : void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
    1108             :                           Direction dir) {
    1109           0 :     SkRRect rrect;
    1110           0 :     rrect.setRectRadii(rect, (const SkVector*) radii);
    1111           0 :     this->addRRect(rrect, dir);
    1112           0 : }
    1113             : 
    1114           0 : void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
    1115             :     // legacy start indices: 6 (CW) and 7(CCW)
    1116           0 :     this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
    1117           0 : }
    1118             : 
    1119           0 : void SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
    1120           0 :         assert_known_direction(dir);
    1121             : 
    1122           0 :         if (rrect.isEmpty()) {
    1123           0 :             return;
    1124             :         }
    1125             : 
    1126           0 :         bool isRRect = hasOnlyMoveTos();
    1127           0 :         const SkRect& bounds = rrect.getBounds();
    1128             : 
    1129           0 :         if (rrect.isRect()) {
    1130             :             // degenerate(rect) => radii points are collapsing
    1131           0 :             this->addRect(bounds, dir, (startIndex + 1) / 2);
    1132           0 :         } else if (rrect.isOval()) {
    1133             :             // degenerate(oval) => line points are collapsing
    1134           0 :             this->addOval(bounds, dir, startIndex / 2);
    1135             :         } else {
    1136           0 :             fFirstDirection = this->hasOnlyMoveTos() ?
    1137           0 :                                 (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
    1138             : 
    1139           0 :             SkAutoPathBoundsUpdate apbu(this, bounds);
    1140           0 :             SkAutoDisableDirectionCheck addc(this);
    1141             : 
    1142             :             // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
    1143           0 :             const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
    1144           0 :             const SkScalar weight = SK_ScalarRoot2Over2;
    1145             : 
    1146           0 :             SkDEBUGCODE(int initialVerbCount = this->countVerbs());
    1147             :             const int kVerbs = startsWithConic
    1148           0 :                 ? 9   // moveTo + 4x conicTo + 3x lineTo + close
    1149           0 :                 : 10; // moveTo + 4x lineTo + 4x conicTo + close
    1150           0 :             this->incReserve(kVerbs);
    1151             : 
    1152           0 :             RRectPointIterator rrectIter(rrect, dir, startIndex);
    1153             :             // Corner iterator indices follow the collapsed radii model,
    1154             :             // adjusted such that the start pt is "behind" the radii start pt.
    1155           0 :             const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
    1156           0 :             RectPointIterator rectIter(bounds, dir, rectStartIndex);
    1157             : 
    1158           0 :             this->moveTo(rrectIter.current());
    1159           0 :             if (startsWithConic) {
    1160           0 :                 for (unsigned i = 0; i < 3; ++i) {
    1161           0 :                     this->conicTo(rectIter.next(), rrectIter.next(), weight);
    1162           0 :                     this->lineTo(rrectIter.next());
    1163             :                 }
    1164           0 :                 this->conicTo(rectIter.next(), rrectIter.next(), weight);
    1165             :                 // final lineTo handled by close().
    1166             :             } else {
    1167           0 :                 for (unsigned i = 0; i < 4; ++i) {
    1168           0 :                     this->lineTo(rrectIter.next());
    1169           0 :                     this->conicTo(rectIter.next(), rrectIter.next(), weight);
    1170             :                 }
    1171             :             }
    1172           0 :             this->close();
    1173             : 
    1174           0 :             SkPathRef::Editor ed(&fPathRef);
    1175           0 :             ed.setIsRRect(isRRect, dir, startIndex % 8);
    1176             : 
    1177           0 :             SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
    1178             :         }
    1179             : 
    1180           0 :         SkDEBUGCODE(fPathRef->validate();)
    1181             : }
    1182             : 
    1183         113 : bool SkPath::hasOnlyMoveTos() const {
    1184         113 :     int count = fPathRef->countVerbs();
    1185         113 :     const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
    1186         113 :     for (int i = 0; i < count; ++i) {
    1187           0 :         if (*verbs == kLine_Verb ||
    1188           0 :             *verbs == kQuad_Verb ||
    1189           0 :             *verbs == kConic_Verb ||
    1190           0 :             *verbs == kCubic_Verb) {
    1191           0 :             return false;
    1192             :         }
    1193           0 :         ++verbs;
    1194             :     }
    1195         113 :     return true;
    1196             : }
    1197             : 
    1198           0 : bool SkPath::isZeroLength() const {
    1199           0 :     int count = fPathRef->countPoints();
    1200           0 :     if (count < 2) {
    1201           0 :         return true;
    1202             :     }
    1203           0 :     const SkPoint* pts = fPathRef.get()->points();
    1204           0 :     const SkPoint& first = *pts;
    1205           0 :     for (int index = 1; index < count; ++index) {
    1206           0 :         if (first != pts[index]) {
    1207           0 :             return false;
    1208             :         }
    1209             :     }
    1210           0 :     return true;
    1211             : }
    1212             : 
    1213           0 : void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
    1214             :                           Direction dir) {
    1215           0 :     assert_known_direction(dir);
    1216             : 
    1217           0 :     if (rx < 0 || ry < 0) {
    1218           0 :         return;
    1219             :     }
    1220             : 
    1221           0 :     SkRRect rrect;
    1222           0 :     rrect.setRectXY(rect, rx, ry);
    1223           0 :     this->addRRect(rrect, dir);
    1224             : }
    1225             : 
    1226           0 : void SkPath::addOval(const SkRect& oval, Direction dir) {
    1227             :     // legacy start index: 1
    1228           0 :     this->addOval(oval, dir, 1);
    1229           0 : }
    1230             : 
    1231           0 : void SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
    1232           0 :     assert_known_direction(dir);
    1233             : 
    1234             :     /* If addOval() is called after previous moveTo(),
    1235             :        this path is still marked as an oval. This is used to
    1236             :        fit into WebKit's calling sequences.
    1237             :        We can't simply check isEmpty() in this case, as additional
    1238             :        moveTo() would mark the path non empty.
    1239             :      */
    1240           0 :     bool isOval = hasOnlyMoveTos();
    1241           0 :     if (isOval) {
    1242           0 :         fFirstDirection = (SkPathPriv::FirstDirection)dir;
    1243             :     } else {
    1244           0 :         fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
    1245             :     }
    1246             : 
    1247           0 :     SkAutoDisableDirectionCheck addc(this);
    1248           0 :     SkAutoPathBoundsUpdate apbu(this, oval);
    1249             : 
    1250           0 :     SkDEBUGCODE(int initialVerbCount = this->countVerbs());
    1251           0 :     const int kVerbs = 6; // moveTo + 4x conicTo + close
    1252           0 :     this->incReserve(kVerbs);
    1253             : 
    1254           0 :     OvalPointIterator ovalIter(oval, dir, startPointIndex);
    1255             :     // The corner iterator pts are tracking "behind" the oval/radii pts.
    1256           0 :     RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
    1257           0 :     const SkScalar weight = SK_ScalarRoot2Over2;
    1258             : 
    1259           0 :     this->moveTo(ovalIter.current());
    1260           0 :     for (unsigned i = 0; i < 4; ++i) {
    1261           0 :         this->conicTo(rectIter.next(), ovalIter.next(), weight);
    1262             :     }
    1263           0 :     this->close();
    1264             : 
    1265           0 :     SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
    1266             : 
    1267           0 :     SkPathRef::Editor ed(&fPathRef);
    1268             : 
    1269           0 :     ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4);
    1270           0 : }
    1271             : 
    1272           0 : void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
    1273           0 :     if (r > 0) {
    1274           0 :         this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
    1275             :     }
    1276           0 : }
    1277             : 
    1278           0 : void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
    1279             :                    bool forceMoveTo) {
    1280           0 :     if (oval.width() < 0 || oval.height() < 0) {
    1281           0 :         return;
    1282             :     }
    1283             : 
    1284           0 :     if (fPathRef->countVerbs() == 0) {
    1285           0 :         forceMoveTo = true;
    1286             :     }
    1287             : 
    1288             :     SkPoint lonePt;
    1289           0 :     if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
    1290           0 :         forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
    1291           0 :         return;
    1292             :     }
    1293             : 
    1294             :     SkVector startV, stopV;
    1295             :     SkRotationDirection dir;
    1296           0 :     angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
    1297             : 
    1298             :     SkPoint singlePt;
    1299             : 
    1300             :     // At this point, we know that the arc is not a lone point, but startV == stopV
    1301             :     // indicates that the sweepAngle is too small such that angles_to_unit_vectors
    1302             :     // cannot handle it.
    1303           0 :     if (startV == stopV) {
    1304           0 :         SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
    1305           0 :         SkScalar radiusX = oval.width() / 2;
    1306           0 :         SkScalar radiusY = oval.height() / 2;
    1307             :         // We cannot use SkScalarSinCos function in the next line because
    1308             :         // SkScalarSinCos has a threshold *SkScalarNearlyZero*. When sin(startAngle)
    1309             :         // is 0 and sweepAngle is very small and radius is huge, the expected
    1310             :         // behavior here is to draw a line. But calling SkScalarSinCos will
    1311             :         // make sin(endAngle) to be 0 which will then draw a dot.
    1312           0 :         singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
    1313           0 :             oval.centerY() + radiusY * sk_float_sin(endAngle));
    1314           0 :         forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
    1315           0 :         return;
    1316             :     }
    1317             : 
    1318           0 :     SkConic conics[SkConic::kMaxConicsForArc];
    1319           0 :     int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
    1320           0 :     if (count) {
    1321           0 :         this->incReserve(count * 2 + 1);
    1322           0 :         const SkPoint& pt = conics[0].fPts[0];
    1323           0 :         forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
    1324           0 :         for (int i = 0; i < count; ++i) {
    1325           0 :             this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
    1326             :         }
    1327             :     } else {
    1328           0 :         forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
    1329             :     }
    1330             : }
    1331             : 
    1332             : // This converts the SVG arc to conics.
    1333             : // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
    1334             : // Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
    1335             : // See also SVG implementation notes:
    1336             : // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
    1337             : // Note that arcSweep bool value is flipped from the original implementation.
    1338           0 : void SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
    1339             :                    SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
    1340           0 :     this->injectMoveToIfNeeded();
    1341             :     SkPoint srcPts[2];
    1342           0 :     this->getLastPt(&srcPts[0]);
    1343             :     // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
    1344             :     // joining the endpoints.
    1345             :     // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
    1346           0 :     if (!rx || !ry) {
    1347           0 :         this->lineTo(x, y);
    1348           0 :         return;
    1349             :     }
    1350             :     // If the current point and target point for the arc are identical, it should be treated as a
    1351             :     // zero length path. This ensures continuity in animations.
    1352           0 :     srcPts[1].set(x, y);
    1353           0 :     if (srcPts[0] == srcPts[1]) {
    1354           0 :         this->lineTo(x, y);
    1355           0 :         return;
    1356             :     }
    1357           0 :     rx = SkScalarAbs(rx);
    1358           0 :     ry = SkScalarAbs(ry);
    1359           0 :     SkVector midPointDistance = srcPts[0] - srcPts[1];
    1360           0 :     midPointDistance *= 0.5f;
    1361             : 
    1362             :     SkMatrix pointTransform;
    1363           0 :     pointTransform.setRotate(-angle);
    1364             : 
    1365             :     SkPoint transformedMidPoint;
    1366           0 :     pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
    1367           0 :     SkScalar squareRx = rx * rx;
    1368           0 :     SkScalar squareRy = ry * ry;
    1369           0 :     SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
    1370           0 :     SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
    1371             : 
    1372             :     // Check if the radii are big enough to draw the arc, scale radii if not.
    1373             :     // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
    1374           0 :     SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
    1375           0 :     if (radiiScale > 1) {
    1376           0 :         radiiScale = SkScalarSqrt(radiiScale);
    1377           0 :         rx *= radiiScale;
    1378           0 :         ry *= radiiScale;
    1379             :     }
    1380             : 
    1381           0 :     pointTransform.setScale(1 / rx, 1 / ry);
    1382           0 :     pointTransform.preRotate(-angle);
    1383             : 
    1384             :     SkPoint unitPts[2];
    1385           0 :     pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
    1386           0 :     SkVector delta = unitPts[1] - unitPts[0];
    1387             : 
    1388           0 :     SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
    1389           0 :     SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
    1390             : 
    1391           0 :     SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
    1392           0 :     if (SkToBool(arcSweep) != SkToBool(arcLarge)) {  // flipped from the original implementation
    1393           0 :         scaleFactor = -scaleFactor;
    1394             :     }
    1395           0 :     delta.scale(scaleFactor);
    1396           0 :     SkPoint centerPoint = unitPts[0] + unitPts[1];
    1397           0 :     centerPoint *= 0.5f;
    1398           0 :     centerPoint.offset(-delta.fY, delta.fX);
    1399           0 :     unitPts[0] -= centerPoint;
    1400           0 :     unitPts[1] -= centerPoint;
    1401           0 :     SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
    1402           0 :     SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
    1403           0 :     SkScalar thetaArc = theta2 - theta1;
    1404           0 :     if (thetaArc < 0 && !arcSweep) {  // arcSweep flipped from the original implementation
    1405           0 :         thetaArc += SK_ScalarPI * 2;
    1406           0 :     } else if (thetaArc > 0 && arcSweep) {  // arcSweep flipped from the original implementation
    1407           0 :         thetaArc -= SK_ScalarPI * 2;
    1408             :     }
    1409           0 :     pointTransform.setRotate(angle);
    1410           0 :     pointTransform.preScale(rx, ry);
    1411             : 
    1412           0 :     int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2)));
    1413           0 :     SkScalar thetaWidth = thetaArc / segments;
    1414           0 :     SkScalar t = SkScalarTan(0.5f * thetaWidth);
    1415           0 :     if (!SkScalarIsFinite(t)) {
    1416           0 :         return;
    1417             :     }
    1418           0 :     SkScalar startTheta = theta1;
    1419           0 :     SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
    1420           0 :     for (int i = 0; i < segments; ++i) {
    1421           0 :         SkScalar endTheta = startTheta + thetaWidth;
    1422           0 :         SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
    1423             : 
    1424           0 :         unitPts[1].set(cosEndTheta, sinEndTheta);
    1425           0 :         unitPts[1] += centerPoint;
    1426           0 :         unitPts[0] = unitPts[1];
    1427           0 :         unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
    1428             :         SkPoint mapped[2];
    1429           0 :         pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
    1430           0 :         this->conicTo(mapped[0], mapped[1], w);
    1431           0 :         startTheta = endTheta;
    1432             :     }
    1433             : }
    1434             : 
    1435           0 : void SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
    1436             :                     SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
    1437             :     SkPoint currentPoint;
    1438           0 :     this->getLastPt(&currentPoint);
    1439           0 :     this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, currentPoint.fX + dx, currentPoint.fY + dy);
    1440           0 : }
    1441             : 
    1442           0 : void SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
    1443           0 :     if (oval.isEmpty() || 0 == sweepAngle) {
    1444           0 :         return;
    1445             :     }
    1446             : 
    1447           0 :     const SkScalar kFullCircleAngle = SkIntToScalar(360);
    1448             : 
    1449           0 :     if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
    1450             :         // We can treat the arc as an oval if it begins at one of our legal starting positions.
    1451             :         // See SkPath::addOval() docs.
    1452           0 :         SkScalar startOver90 = startAngle / 90.f;
    1453           0 :         SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
    1454           0 :         SkScalar error = startOver90 - startOver90I;
    1455           0 :         if (SkScalarNearlyEqual(error, 0)) {
    1456             :             // Index 1 is at startAngle == 0.
    1457           0 :             SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
    1458           0 :             startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
    1459           0 :             this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction,
    1460           0 :                           (unsigned) startIndex);
    1461           0 :             return;
    1462             :         }
    1463             :     }
    1464           0 :     this->arcTo(oval, startAngle, sweepAngle, true);
    1465             : }
    1466             : 
    1467             : /*
    1468             :     Need to handle the case when the angle is sharp, and our computed end-points
    1469             :     for the arc go behind pt1 and/or p2...
    1470             : */
    1471           0 : void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
    1472           0 :     if (radius == 0) {
    1473           0 :         this->lineTo(x1, y1);
    1474           0 :         return;
    1475             :     }
    1476             : 
    1477             :     SkVector before, after;
    1478             : 
    1479             :     // need to know our prev pt so we can construct tangent vectors
    1480             :     {
    1481             :         SkPoint start;
    1482           0 :         this->getLastPt(&start);
    1483             :         // Handle degenerate cases by adding a line to the first point and
    1484             :         // bailing out.
    1485           0 :         before.setNormalize(x1 - start.fX, y1 - start.fY);
    1486           0 :         after.setNormalize(x2 - x1, y2 - y1);
    1487             :     }
    1488             : 
    1489           0 :     SkScalar cosh = SkPoint::DotProduct(before, after);
    1490           0 :     SkScalar sinh = SkPoint::CrossProduct(before, after);
    1491             : 
    1492           0 :     if (SkScalarNearlyZero(sinh)) {   // angle is too tight
    1493           0 :         this->lineTo(x1, y1);
    1494           0 :         return;
    1495             :     }
    1496             : 
    1497           0 :     SkScalar dist = SkScalarAbs(radius * (1 - cosh) / sinh);
    1498             : 
    1499           0 :     SkScalar xx = x1 - dist * before.fX;
    1500           0 :     SkScalar yy = y1 - dist * before.fY;
    1501           0 :     after.setLength(dist);
    1502           0 :     this->lineTo(xx, yy);
    1503           0 :     SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf);
    1504           0 :     this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
    1505             : }
    1506             : 
    1507             : ///////////////////////////////////////////////////////////////////////////////
    1508             : 
    1509           0 : void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
    1510             :     SkMatrix matrix;
    1511             : 
    1512           0 :     matrix.setTranslate(dx, dy);
    1513           0 :     this->addPath(path, matrix, mode);
    1514           0 : }
    1515             : 
    1516          15 : void SkPath::addPath(const SkPath& path, const SkMatrix& matrix, AddPathMode mode) {
    1517          15 :     SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
    1518             : 
    1519          15 :     RawIter iter(path);
    1520             :     SkPoint pts[4];
    1521             :     Verb    verb;
    1522             : 
    1523          15 :     SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
    1524          15 :     bool firstVerb = true;
    1525          15 :     while ((verb = iter.next(pts)) != kDone_Verb) {
    1526           0 :         switch (verb) {
    1527             :             case kMove_Verb:
    1528           0 :                 proc(matrix, &pts[0], &pts[0], 1);
    1529           0 :                 if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
    1530           0 :                     injectMoveToIfNeeded(); // In case last contour is closed
    1531           0 :                     this->lineTo(pts[0]);
    1532             :                 } else {
    1533           0 :                     this->moveTo(pts[0]);
    1534             :                 }
    1535           0 :                 break;
    1536             :             case kLine_Verb:
    1537           0 :                 proc(matrix, &pts[1], &pts[1], 1);
    1538           0 :                 this->lineTo(pts[1]);
    1539           0 :                 break;
    1540             :             case kQuad_Verb:
    1541           0 :                 proc(matrix, &pts[1], &pts[1], 2);
    1542           0 :                 this->quadTo(pts[1], pts[2]);
    1543           0 :                 break;
    1544             :             case kConic_Verb:
    1545           0 :                 proc(matrix, &pts[1], &pts[1], 2);
    1546           0 :                 this->conicTo(pts[1], pts[2], iter.conicWeight());
    1547           0 :                 break;
    1548             :             case kCubic_Verb:
    1549           0 :                 proc(matrix, &pts[1], &pts[1], 3);
    1550           0 :                 this->cubicTo(pts[1], pts[2], pts[3]);
    1551           0 :                 break;
    1552             :             case kClose_Verb:
    1553           0 :                 this->close();
    1554           0 :                 break;
    1555             :             default:
    1556           0 :                 SkDEBUGFAIL("unknown verb");
    1557             :         }
    1558           0 :         firstVerb = false;
    1559             :     }
    1560          15 : }
    1561             : 
    1562             : ///////////////////////////////////////////////////////////////////////////////
    1563             : 
    1564          29 : static int pts_in_verb(unsigned verb) {
    1565             :     static const uint8_t gPtsInVerb[] = {
    1566             :         1,  // kMove
    1567             :         1,  // kLine
    1568             :         2,  // kQuad
    1569             :         2,  // kConic
    1570             :         3,  // kCubic
    1571             :         0,  // kClose
    1572             :         0   // kDone
    1573             :     };
    1574             : 
    1575          29 :     SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
    1576          29 :     return gPtsInVerb[verb];
    1577             : }
    1578             : 
    1579             : // ignore the last point of the 1st contour
    1580          15 : void SkPath::reversePathTo(const SkPath& path) {
    1581          15 :     const uint8_t* verbs = path.fPathRef->verbsMemBegin(); // points at the last verb
    1582          15 :     if (!verbs) {  // empty path returns nullptr
    1583           0 :         return;
    1584             :     }
    1585          15 :     const uint8_t* verbsEnd = path.fPathRef->verbs() - 1; // points just past the first verb
    1586          15 :     SkASSERT(verbsEnd[0] == kMove_Verb);
    1587          15 :     const SkPoint*  pts = path.fPathRef->pointsEnd() - 1;
    1588          15 :     const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
    1589             : 
    1590          73 :     while (verbs < verbsEnd) {
    1591          29 :         uint8_t v = *verbs++;
    1592          29 :         pts -= pts_in_verb(v);
    1593          29 :         switch (v) {
    1594             :             case kMove_Verb:
    1595             :                 // if the path has multiple contours, stop after reversing the last
    1596           0 :                 return;
    1597             :             case kLine_Verb:
    1598          21 :                 this->lineTo(pts[0]);
    1599          21 :                 break;
    1600             :             case kQuad_Verb:
    1601           8 :                 this->quadTo(pts[1], pts[0]);
    1602           8 :                 break;
    1603             :             case kConic_Verb:
    1604           0 :                 this->conicTo(pts[1], pts[0], *--conicWeights);
    1605           0 :                 break;
    1606             :             case kCubic_Verb:
    1607           0 :                 this->cubicTo(pts[2], pts[1], pts[0]);
    1608           0 :                 break;
    1609             :             case kClose_Verb:
    1610           0 :                 SkASSERT(verbs - path.fPathRef->verbsMemBegin() == 1);
    1611           0 :                 break;
    1612             :             default:
    1613           0 :                 SkDEBUGFAIL("bad verb");
    1614           0 :                 break;
    1615             :         }
    1616             :     }
    1617             : }
    1618             : 
    1619           0 : void SkPath::reverseAddPath(const SkPath& src) {
    1620           0 :     SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
    1621             : 
    1622           0 :     const SkPoint* pts = src.fPathRef->pointsEnd();
    1623             :     // we will iterator through src's verbs backwards
    1624           0 :     const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
    1625           0 :     const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
    1626           0 :     const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
    1627             : 
    1628           0 :     bool needMove = true;
    1629           0 :     bool needClose = false;
    1630           0 :     while (verbs < verbsEnd) {
    1631           0 :         uint8_t v = *(verbs++);
    1632           0 :         int n = pts_in_verb(v);
    1633             : 
    1634           0 :         if (needMove) {
    1635           0 :             --pts;
    1636           0 :             this->moveTo(pts->fX, pts->fY);
    1637           0 :             needMove = false;
    1638             :         }
    1639           0 :         pts -= n;
    1640           0 :         switch (v) {
    1641             :             case kMove_Verb:
    1642           0 :                 if (needClose) {
    1643           0 :                     this->close();
    1644           0 :                     needClose = false;
    1645             :                 }
    1646           0 :                 needMove = true;
    1647           0 :                 pts += 1;   // so we see the point in "if (needMove)" above
    1648           0 :                 break;
    1649             :             case kLine_Verb:
    1650           0 :                 this->lineTo(pts[0]);
    1651           0 :                 break;
    1652             :             case kQuad_Verb:
    1653           0 :                 this->quadTo(pts[1], pts[0]);
    1654           0 :                 break;
    1655             :             case kConic_Verb:
    1656           0 :                 this->conicTo(pts[1], pts[0], *--conicWeights);
    1657           0 :                 break;
    1658             :             case kCubic_Verb:
    1659           0 :                 this->cubicTo(pts[2], pts[1], pts[0]);
    1660           0 :                 break;
    1661             :             case kClose_Verb:
    1662           0 :                 needClose = true;
    1663           0 :                 break;
    1664             :             default:
    1665           0 :                 SkDEBUGFAIL("unexpected verb");
    1666             :         }
    1667             :     }
    1668           0 : }
    1669             : 
    1670             : ///////////////////////////////////////////////////////////////////////////////
    1671             : 
    1672           0 : void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
    1673             :     SkMatrix    matrix;
    1674             : 
    1675           0 :     matrix.setTranslate(dx, dy);
    1676           0 :     this->transform(matrix, dst);
    1677           0 : }
    1678             : 
    1679           0 : static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
    1680             :                                int level = 2) {
    1681           0 :     if (--level >= 0) {
    1682             :         SkPoint tmp[7];
    1683             : 
    1684           0 :         SkChopCubicAtHalf(pts, tmp);
    1685           0 :         subdivide_cubic_to(path, &tmp[0], level);
    1686           0 :         subdivide_cubic_to(path, &tmp[3], level);
    1687             :     } else {
    1688           0 :         path->cubicTo(pts[1], pts[2], pts[3]);
    1689             :     }
    1690           0 : }
    1691             : 
    1692          88 : void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
    1693          88 :     SkDEBUGCODE(this->validate();)
    1694          88 :     if (dst == nullptr) {
    1695           0 :         dst = (SkPath*)this;
    1696             :     }
    1697             : 
    1698          88 :     if (matrix.hasPerspective()) {
    1699           0 :         SkPath  tmp;
    1700           0 :         tmp.fFillType = fFillType;
    1701             : 
    1702           0 :         SkPath::Iter    iter(*this, false);
    1703             :         SkPoint         pts[4];
    1704             :         SkPath::Verb    verb;
    1705             : 
    1706           0 :         while ((verb = iter.next(pts, false)) != kDone_Verb) {
    1707           0 :             switch (verb) {
    1708             :                 case kMove_Verb:
    1709           0 :                     tmp.moveTo(pts[0]);
    1710           0 :                     break;
    1711             :                 case kLine_Verb:
    1712           0 :                     tmp.lineTo(pts[1]);
    1713           0 :                     break;
    1714             :                 case kQuad_Verb:
    1715             :                     // promote the quad to a conic
    1716           0 :                     tmp.conicTo(pts[1], pts[2],
    1717           0 :                                 SkConic::TransformW(pts, SK_Scalar1, matrix));
    1718           0 :                     break;
    1719             :                 case kConic_Verb:
    1720           0 :                     tmp.conicTo(pts[1], pts[2],
    1721           0 :                                 SkConic::TransformW(pts, iter.conicWeight(), matrix));
    1722           0 :                     break;
    1723             :                 case kCubic_Verb:
    1724           0 :                     subdivide_cubic_to(&tmp, pts);
    1725           0 :                     break;
    1726             :                 case kClose_Verb:
    1727           0 :                     tmp.close();
    1728           0 :                     break;
    1729             :                 default:
    1730           0 :                     SkDEBUGFAIL("unknown verb");
    1731           0 :                     break;
    1732             :             }
    1733             :         }
    1734             : 
    1735           0 :         dst->swap(tmp);
    1736           0 :         SkPathRef::Editor ed(&dst->fPathRef);
    1737           0 :         matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
    1738           0 :         dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
    1739             :     } else {
    1740          88 :         SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
    1741             : 
    1742          88 :         if (this != dst) {
    1743          73 :             dst->fFillType = fFillType;
    1744          73 :             dst->fConvexity = fConvexity;
    1745          73 :             dst->fIsVolatile = fIsVolatile;
    1746             :         }
    1747             : 
    1748          88 :         if (SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
    1749          88 :             dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
    1750             :         } else {
    1751             :             SkScalar det2x2 =
    1752           0 :                 matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) -
    1753           0 :                 matrix.get(SkMatrix::kMSkewX)  * matrix.get(SkMatrix::kMSkewY);
    1754           0 :             if (det2x2 < 0) {
    1755           0 :                 dst->fFirstDirection = SkPathPriv::OppositeFirstDirection(
    1756           0 :                         (SkPathPriv::FirstDirection)fFirstDirection.load());
    1757           0 :             } else if (det2x2 > 0) {
    1758           0 :                 dst->fFirstDirection = fFirstDirection.load();
    1759             :             } else {
    1760           0 :                 dst->fConvexity = kUnknown_Convexity;
    1761           0 :                 dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
    1762             :             }
    1763             :         }
    1764             : 
    1765          88 :         SkDEBUGCODE(dst->validate();)
    1766             :     }
    1767          88 : }
    1768             : 
    1769             : ///////////////////////////////////////////////////////////////////////////////
    1770             : ///////////////////////////////////////////////////////////////////////////////
    1771             : 
    1772             : enum SegmentState {
    1773             :     kEmptyContour_SegmentState,   // The current contour is empty. We may be
    1774             :                                   // starting processing or we may have just
    1775             :                                   // closed a contour.
    1776             :     kAfterMove_SegmentState,      // We have seen a move, but nothing else.
    1777             :     kAfterPrimitive_SegmentState  // We have seen a primitive but not yet
    1778             :                                   // closed the path. Also the initial state.
    1779             : };
    1780             : 
    1781           0 : SkPath::Iter::Iter() {
    1782             : #ifdef SK_DEBUG
    1783           0 :     fPts = nullptr;
    1784           0 :     fConicWeights = nullptr;
    1785           0 :     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
    1786           0 :     fForceClose = fCloseLine = false;
    1787           0 :     fSegmentState = kEmptyContour_SegmentState;
    1788             : #endif
    1789             :     // need to init enough to make next() harmlessly return kDone_Verb
    1790           0 :     fVerbs = nullptr;
    1791           0 :     fVerbStop = nullptr;
    1792           0 :     fNeedClose = false;
    1793           0 : }
    1794             : 
    1795         467 : SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
    1796         467 :     this->setPath(path, forceClose);
    1797         467 : }
    1798             : 
    1799         467 : void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
    1800         467 :     fPts = path.fPathRef->points();
    1801         467 :     fVerbs = path.fPathRef->verbs();
    1802         467 :     fVerbStop = path.fPathRef->verbsMemBegin();
    1803         467 :     fConicWeights = path.fPathRef->conicWeights();
    1804         467 :     if (fConicWeights) {
    1805           0 :       fConicWeights -= 1;  // begin one behind
    1806             :     }
    1807         467 :     fLastPt.fX = fLastPt.fY = 0;
    1808         467 :     fMoveTo.fX = fMoveTo.fY = 0;
    1809         467 :     fForceClose = SkToU8(forceClose);
    1810         467 :     fNeedClose = false;
    1811         467 :     fSegmentState = kEmptyContour_SegmentState;
    1812         467 : }
    1813             : 
    1814           0 : bool SkPath::Iter::isClosedContour() const {
    1815           0 :     if (fVerbs == nullptr || fVerbs == fVerbStop) {
    1816           0 :         return false;
    1817             :     }
    1818           0 :     if (fForceClose) {
    1819           0 :         return true;
    1820             :     }
    1821             : 
    1822           0 :     const uint8_t* verbs = fVerbs;
    1823           0 :     const uint8_t* stop = fVerbStop;
    1824             : 
    1825           0 :     if (kMove_Verb == *(verbs - 1)) {
    1826           0 :         verbs -= 1; // skip the initial moveto
    1827             :     }
    1828             : 
    1829           0 :     while (verbs > stop) {
    1830             :         // verbs points one beyond the current verb, decrement first.
    1831           0 :         unsigned v = *(--verbs);
    1832           0 :         if (kMove_Verb == v) {
    1833           0 :             break;
    1834             :         }
    1835           0 :         if (kClose_Verb == v) {
    1836           0 :             return true;
    1837             :         }
    1838             :     }
    1839           0 :     return false;
    1840             : }
    1841             : 
    1842         627 : SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
    1843         627 :     SkASSERT(pts);
    1844         627 :     if (fLastPt != fMoveTo) {
    1845             :         // A special case: if both points are NaN, SkPoint::operation== returns
    1846             :         // false, but the iterator expects that they are treated as the same.
    1847             :         // (consider SkPoint is a 2-dimension float point).
    1848         912 :         if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
    1849         684 :             SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
    1850           0 :             return kClose_Verb;
    1851             :         }
    1852             : 
    1853         228 :         pts[0] = fLastPt;
    1854         228 :         pts[1] = fMoveTo;
    1855         228 :         fLastPt = fMoveTo;
    1856         228 :         fCloseLine = true;
    1857         228 :         return kLine_Verb;
    1858             :     } else {
    1859         399 :         pts[0] = fMoveTo;
    1860         399 :         return kClose_Verb;
    1861             :     }
    1862             : }
    1863             : 
    1864        2136 : const SkPoint& SkPath::Iter::cons_moveTo() {
    1865        2136 :     if (fSegmentState == kAfterMove_SegmentState) {
    1866             :         // Set the first return pt to the move pt
    1867         434 :         fSegmentState = kAfterPrimitive_SegmentState;
    1868         434 :         return fMoveTo;
    1869             :     } else {
    1870        1702 :         SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
    1871             :          // Set the first return pt to the last pt of the previous primitive.
    1872        1702 :         return fPts[-1];
    1873             :     }
    1874             : }
    1875             : 
    1876         963 : void SkPath::Iter::consumeDegenerateSegments(bool exact) {
    1877             :     // We need to step over anything that will not move the current draw point
    1878             :     // forward before the next move is seen
    1879         963 :     const uint8_t* lastMoveVerb = 0;
    1880         963 :     const SkPoint* lastMovePt = 0;
    1881         963 :     const SkScalar* lastMoveWeight = nullptr;
    1882         963 :     SkPoint lastPt = fLastPt;
    1883        1481 :     while (fVerbs != fVerbStop) {
    1884        1068 :         unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
    1885        1068 :         switch (verb) {
    1886             :             case kMove_Verb:
    1887             :                 // Keep a record of this most recent move
    1888         163 :                 lastMoveVerb = fVerbs;
    1889         163 :                 lastMovePt = fPts;
    1890         163 :                 lastMoveWeight = fConicWeights;
    1891         163 :                 lastPt = fPts[0];
    1892         163 :                 fVerbs--;
    1893         163 :                 fPts++;
    1894         163 :                 break;
    1895             : 
    1896             :             case kClose_Verb:
    1897             :                 // A close when we are in a segment is always valid except when it
    1898             :                 // follows a move which follows a segment.
    1899         121 :                 if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
    1900         930 :                     return;
    1901             :                 }
    1902             :                 // A close at any other time must be ignored
    1903           0 :                 fVerbs--;
    1904           0 :                 break;
    1905             : 
    1906             :             case kLine_Verb:
    1907         543 :                 if (!IsLineDegenerate(lastPt, fPts[0], exact)) {
    1908         447 :                     if (lastMoveVerb) {
    1909         129 :                         fVerbs = lastMoveVerb;
    1910         129 :                         fPts = lastMovePt;
    1911         129 :                         fConicWeights = lastMoveWeight;
    1912         129 :                         return;
    1913             :                     }
    1914         318 :                     return;
    1915             :                 }
    1916             :                 // Ignore this line and continue
    1917          96 :                 fVerbs--;
    1918          96 :                 fPts++;
    1919          96 :                 break;
    1920             : 
    1921             :             case kConic_Verb:
    1922             :             case kQuad_Verb:
    1923          10 :                 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1], exact)) {
    1924          10 :                     if (lastMoveVerb) {
    1925           2 :                         fVerbs = lastMoveVerb;
    1926           2 :                         fPts = lastMovePt;
    1927           2 :                         fConicWeights = lastMoveWeight;
    1928           2 :                         return;
    1929             :                     }
    1930           8 :                     return;
    1931             :                 }
    1932             :                 // Ignore this line and continue
    1933           0 :                 fVerbs--;
    1934           0 :                 fPts += 2;
    1935           0 :                 fConicWeights += (kConic_Verb == verb);
    1936           0 :                 break;
    1937             : 
    1938             :             case kCubic_Verb:
    1939         231 :                 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2], exact)) {
    1940         231 :                     if (lastMoveVerb) {
    1941          32 :                         fVerbs = lastMoveVerb;
    1942          32 :                         fPts = lastMovePt;
    1943          32 :                         fConicWeights = lastMoveWeight;
    1944          32 :                         return;
    1945             :                     }
    1946         199 :                     return;
    1947             :                 }
    1948             :                 // Ignore this line and continue
    1949           0 :                 fVerbs--;
    1950           0 :                 fPts += 3;
    1951           0 :                 break;
    1952             : 
    1953             :             default:
    1954           0 :                 SkDEBUGFAIL("Should never see kDone_Verb");
    1955             :         }
    1956             :     }
    1957             : }
    1958             : 
    1959        3642 : SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
    1960        3642 :     SkASSERT(ptsParam);
    1961             : 
    1962        3642 :     if (fVerbs == fVerbStop) {
    1963             :         // Close the curve if requested and if there is some curve to close
    1964         395 :         if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
    1965           0 :             if (kLine_Verb == this->autoClose(ptsParam)) {
    1966           0 :                 return kLine_Verb;
    1967             :             }
    1968           0 :             fNeedClose = false;
    1969           0 :             return kClose_Verb;
    1970             :         }
    1971         395 :         return kDone_Verb;
    1972             :     }
    1973             : 
    1974             :     // fVerbs is one beyond the current verb, decrement first
    1975        3247 :     unsigned verb = *(--fVerbs);
    1976        3247 :     const SkPoint* SK_RESTRICT srcPts = fPts;
    1977        3247 :     SkPoint* SK_RESTRICT       pts = ptsParam;
    1978             : 
    1979        3247 :     switch (verb) {
    1980             :         case kMove_Verb:
    1981         486 :             if (fNeedClose) {
    1982           2 :                 fVerbs++; // move back one verb
    1983           2 :                 verb = this->autoClose(pts);
    1984           2 :                 if (verb == kClose_Verb) {
    1985           1 :                     fNeedClose = false;
    1986             :                 }
    1987           2 :                 return (Verb)verb;
    1988             :             }
    1989         484 :             if (fVerbs == fVerbStop) {    // might be a trailing moveto
    1990           0 :                 return kDone_Verb;
    1991             :             }
    1992         484 :             fMoveTo = *srcPts;
    1993         484 :             pts[0] = *srcPts;
    1994         484 :             srcPts += 1;
    1995         484 :             fSegmentState = kAfterMove_SegmentState;
    1996         484 :             fLastPt = fMoveTo;
    1997         484 :             fNeedClose = fForceClose;
    1998         484 :             break;
    1999             :         case kLine_Verb:
    2000        1482 :             pts[0] = this->cons_moveTo();
    2001        1482 :             pts[1] = srcPts[0];
    2002        1482 :             fLastPt = srcPts[0];
    2003        1482 :             fCloseLine = false;
    2004        1482 :             srcPts += 1;
    2005        1482 :             break;
    2006             :         case kConic_Verb:
    2007           0 :             fConicWeights += 1;
    2008             :             // fall-through
    2009             :         case kQuad_Verb:
    2010          24 :             pts[0] = this->cons_moveTo();
    2011          24 :             memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
    2012          24 :             fLastPt = srcPts[1];
    2013          24 :             srcPts += 2;
    2014          24 :             break;
    2015             :         case kCubic_Verb:
    2016         630 :             pts[0] = this->cons_moveTo();
    2017         630 :             memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
    2018         630 :             fLastPt = srcPts[2];
    2019         630 :             srcPts += 3;
    2020         630 :             break;
    2021             :         case kClose_Verb:
    2022         625 :             verb = this->autoClose(pts);
    2023         625 :             if (verb == kLine_Verb) {
    2024         227 :                 fVerbs++; // move back one verb
    2025             :             } else {
    2026         398 :                 fNeedClose = false;
    2027         398 :                 fSegmentState = kEmptyContour_SegmentState;
    2028             :             }
    2029         625 :             fLastPt = fMoveTo;
    2030         625 :             break;
    2031             :     }
    2032        3245 :     fPts = srcPts;
    2033        3245 :     return (Verb)verb;
    2034             : }
    2035             : 
    2036             : ///////////////////////////////////////////////////////////////////////////////
    2037             : 
    2038             : /*
    2039             :     Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
    2040             : */
    2041             : 
    2042           0 : size_t SkPath::writeToMemory(void* storage) const {
    2043           0 :     SkDEBUGCODE(this->validate();)
    2044             : 
    2045           0 :     if (nullptr == storage) {
    2046           0 :         const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize();
    2047           0 :         return SkAlign4(byteCount);
    2048             :     }
    2049             : 
    2050           0 :     SkWBuffer   buffer(storage);
    2051             : 
    2052           0 :     int32_t packed = (fConvexity << kConvexity_SerializationShift) |
    2053           0 :                      (fFillType << kFillType_SerializationShift) |
    2054           0 :                      (fFirstDirection << kDirection_SerializationShift) |
    2055           0 :                      (fIsVolatile << kIsVolatile_SerializationShift) |
    2056           0 :                      kCurrent_Version;
    2057             : 
    2058           0 :     buffer.write32(packed);
    2059           0 :     buffer.write32(fLastMoveToIndex);
    2060             : 
    2061           0 :     fPathRef->writeToBuffer(&buffer);
    2062             : 
    2063           0 :     buffer.padToAlign4();
    2064           0 :     return buffer.pos();
    2065             : }
    2066             : 
    2067           0 : size_t SkPath::readFromMemory(const void* storage, size_t length) {
    2068           0 :     SkRBuffer buffer(storage, length);
    2069             : 
    2070             :     int32_t packed;
    2071           0 :     if (!buffer.readS32(&packed)) {
    2072           0 :         return 0;
    2073             :     }
    2074             : 
    2075           0 :     unsigned version = packed & 0xFF;
    2076           0 :     if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) {
    2077           0 :         return 0;
    2078             :     }
    2079             : 
    2080           0 :     fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
    2081           0 :     fFillType = (packed >> kFillType_SerializationShift) & 0x3;
    2082           0 :     uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
    2083           0 :     fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
    2084           0 :     SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
    2085           0 :     if (!pathRef) {
    2086           0 :         return 0;
    2087             :     }
    2088             : 
    2089           0 :     fPathRef.reset(pathRef);
    2090           0 :     SkDEBUGCODE(this->validate();)
    2091           0 :     buffer.skipToAlign4();
    2092             : 
    2093             :     // compatibility check
    2094           0 :     if (version < kPathPrivFirstDirection_Version) {
    2095           0 :         switch (dir) {  // old values
    2096             :             case 0:
    2097           0 :                 fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
    2098           0 :                 break;
    2099             :             case 1:
    2100           0 :                 fFirstDirection = SkPathPriv::kCW_FirstDirection;
    2101           0 :                 break;
    2102             :             case 2:
    2103           0 :                 fFirstDirection = SkPathPriv::kCCW_FirstDirection;
    2104           0 :                 break;
    2105             :             default:
    2106           0 :                 SkASSERT(false);
    2107             :         }
    2108             :     } else {
    2109           0 :         fFirstDirection = dir;
    2110             :     }
    2111             : 
    2112           0 :     return buffer.pos();
    2113             : }
    2114             : 
    2115             : ///////////////////////////////////////////////////////////////////////////////
    2116             : 
    2117             : #include "SkString.h"
    2118             : #include "SkStringUtils.h"
    2119             : #include "SkStream.h"
    2120             : 
    2121           0 : static void append_params(SkString* str, const char label[], const SkPoint pts[],
    2122             :                           int count, SkScalarAsStringType strType, SkScalar conicWeight = -1) {
    2123           0 :     str->append(label);
    2124           0 :     str->append("(");
    2125             : 
    2126           0 :     const SkScalar* values = &pts[0].fX;
    2127           0 :     count *= 2;
    2128             : 
    2129           0 :     for (int i = 0; i < count; ++i) {
    2130           0 :         SkAppendScalar(str, values[i], strType);
    2131           0 :         if (i < count - 1) {
    2132           0 :             str->append(", ");
    2133             :         }
    2134             :     }
    2135           0 :     if (conicWeight >= 0) {
    2136           0 :         str->append(", ");
    2137           0 :         SkAppendScalar(str, conicWeight, strType);
    2138             :     }
    2139           0 :     str->append(");");
    2140           0 :     if (kHex_SkScalarAsStringType == strType) {
    2141           0 :         str->append("  // ");
    2142           0 :         for (int i = 0; i < count; ++i) {
    2143           0 :             SkAppendScalarDec(str, values[i]);
    2144           0 :             if (i < count - 1) {
    2145           0 :                 str->append(", ");
    2146             :             }
    2147             :         }
    2148           0 :         if (conicWeight >= 0) {
    2149           0 :             str->append(", ");
    2150           0 :             SkAppendScalarDec(str, conicWeight);
    2151             :         }
    2152             :     }
    2153           0 :     str->append("\n");
    2154           0 : }
    2155             : 
    2156           0 : void SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const {
    2157           0 :     SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
    2158           0 :     Iter    iter(*this, forceClose);
    2159             :     SkPoint pts[4];
    2160             :     Verb    verb;
    2161             : 
    2162           0 :     SkString builder;
    2163             :     char const * const gFillTypeStrs[] = {
    2164             :         "Winding",
    2165             :         "EvenOdd",
    2166             :         "InverseWinding",
    2167             :         "InverseEvenOdd",
    2168           0 :     };
    2169             :     builder.printf("path.setFillType(SkPath::k%s_FillType);\n",
    2170           0 :             gFillTypeStrs[(int) this->getFillType()]);
    2171           0 :     while ((verb = iter.next(pts, false)) != kDone_Verb) {
    2172           0 :         switch (verb) {
    2173             :             case kMove_Verb:
    2174           0 :                 append_params(&builder, "path.moveTo", &pts[0], 1, asType);
    2175           0 :                 break;
    2176             :             case kLine_Verb:
    2177           0 :                 append_params(&builder, "path.lineTo", &pts[1], 1, asType);
    2178           0 :                 break;
    2179             :             case kQuad_Verb:
    2180           0 :                 append_params(&builder, "path.quadTo", &pts[1], 2, asType);
    2181           0 :                 break;
    2182             :             case kConic_Verb:
    2183           0 :                 append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
    2184           0 :                 break;
    2185             :             case kCubic_Verb:
    2186           0 :                 append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
    2187           0 :                 break;
    2188             :             case kClose_Verb:
    2189           0 :                 builder.append("path.close();\n");
    2190           0 :                 break;
    2191             :             default:
    2192           0 :                 SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
    2193           0 :                 verb = kDone_Verb;  // stop the loop
    2194           0 :                 break;
    2195             :         }
    2196           0 :         if (!wStream && builder.size()) {
    2197           0 :             SkDebugf("%s", builder.c_str());
    2198           0 :             builder.reset();
    2199             :         }
    2200             :     }
    2201           0 :     if (wStream) {
    2202           0 :         wStream->writeText(builder.c_str());
    2203             :     }
    2204           0 : }
    2205             : 
    2206           0 : void SkPath::dump() const {
    2207           0 :     this->dump(nullptr, false, false);
    2208           0 : }
    2209             : 
    2210           0 : void SkPath::dumpHex() const {
    2211           0 :     this->dump(nullptr, false, true);
    2212           0 : }
    2213             : 
    2214             : #ifdef SK_DEBUG
    2215        4496 : void SkPath::validate() const {
    2216        4496 :     SkASSERT((fFillType & ~3) == 0);
    2217             : 
    2218             : #ifdef SK_DEBUG_PATH
    2219             :     if (!fBoundsIsDirty) {
    2220             :         SkRect bounds;
    2221             : 
    2222             :         bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
    2223             :         SkASSERT(SkToBool(fIsFinite) == isFinite);
    2224             : 
    2225             :         if (fPathRef->countPoints() <= 1) {
    2226             :             // if we're empty, fBounds may be empty but translated, so we can't
    2227             :             // necessarily compare to bounds directly
    2228             :             // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
    2229             :             // be [2, 2, 2, 2]
    2230             :             SkASSERT(bounds.isEmpty());
    2231             :             SkASSERT(fBounds.isEmpty());
    2232             :         } else {
    2233             :             if (bounds.isEmpty()) {
    2234             :                 SkASSERT(fBounds.isEmpty());
    2235             :             } else {
    2236             :                 if (!fBounds.isEmpty()) {
    2237             :                     SkASSERT(fBounds.contains(bounds));
    2238             :                 }
    2239             :             }
    2240             :         }
    2241             :     }
    2242             : #endif // SK_DEBUG_PATH
    2243        4496 : }
    2244             : #endif // SK_DEBUG
    2245             : 
    2246             : ///////////////////////////////////////////////////////////////////////////////
    2247             : 
    2248        1912 : static int sign(SkScalar x) { return x < 0; }
    2249             : #define kValueNeverReturnedBySign   2
    2250             : 
    2251             : enum DirChange {
    2252             :     kLeft_DirChange,
    2253             :     kRight_DirChange,
    2254             :     kStraight_DirChange,
    2255             :     kBackwards_DirChange,
    2256             : 
    2257             :     kInvalid_DirChange
    2258             : };
    2259             : 
    2260             : 
    2261         934 : static bool almost_equal(SkScalar compA, SkScalar compB) {
    2262             :     // The error epsilon was empirically derived; worse case round rects
    2263             :     // with a mid point outset by 2x float epsilon in tests had an error
    2264             :     // of 12.
    2265         934 :     const int epsilon = 16;
    2266         934 :     if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
    2267           0 :         return false;
    2268             :     }
    2269             :     // no need to check for small numbers because SkPath::Iter has removed degenerate values
    2270         934 :     int aBits = SkFloatAs2sCompliment(compA);
    2271         934 :     int bBits = SkFloatAs2sCompliment(compB);
    2272         934 :     return aBits < bBits + epsilon && bBits < aBits + epsilon;
    2273             : }
    2274             : 
    2275           0 : static bool approximately_zero_when_compared_to(double x, double y) {
    2276           0 :     return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
    2277             : }
    2278             : 
    2279             : 
    2280             : // only valid for a single contour
    2281             : struct Convexicator {
    2282         113 :     Convexicator()
    2283         113 :     : fPtCount(0)
    2284             :     , fConvexity(SkPath::kConvex_Convexity)
    2285             :     , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
    2286             :     , fIsFinite(true)
    2287         113 :     , fIsCurve(false) {
    2288         113 :         fExpectedDir = kInvalid_DirChange;
    2289             :         // warnings
    2290         113 :         fPriorPt.set(0,0);
    2291         113 :         fLastPt.set(0, 0);
    2292         113 :         fCurrPt.set(0, 0);
    2293         113 :         fLastVec.set(0, 0);
    2294         113 :         fFirstVec.set(0, 0);
    2295             : 
    2296         113 :         fDx = fDy = 0;
    2297         113 :         fSx = fSy = kValueNeverReturnedBySign;
    2298         113 :     }
    2299             : 
    2300         800 :     SkPath::Convexity getConvexity() const { return fConvexity; }
    2301             : 
    2302             :     /** The direction returned is only valid if the path is determined convex */
    2303          41 :     SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
    2304             : 
    2305        1074 :     void addPt(const SkPoint& pt) {
    2306        1074 :         if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
    2307           5 :             return;
    2308             :         }
    2309             : 
    2310        1069 :         if (0 == fPtCount) {
    2311         113 :             fCurrPt = pt;
    2312         113 :             ++fPtCount;
    2313             :         } else {
    2314         956 :             SkVector vec = pt - fCurrPt;
    2315         956 :             SkScalar lengthSqd = vec.lengthSqd();
    2316         956 :             if (!SkScalarIsFinite(lengthSqd)) {
    2317           0 :                 fIsFinite = false;
    2318         956 :             } else if (lengthSqd) {
    2319         956 :                 fPriorPt = fLastPt;
    2320         956 :                 fLastPt = fCurrPt;
    2321         956 :                 fCurrPt = pt;
    2322         956 :                 if (++fPtCount == 2) {
    2323         113 :                     fFirstVec = fLastVec = vec;
    2324             :                 } else {
    2325         843 :                     SkASSERT(fPtCount > 2);
    2326         843 :                     this->addVec(vec);
    2327             :                 }
    2328             : 
    2329         956 :                 int sx = sign(vec.fX);
    2330         956 :                 int sy = sign(vec.fY);
    2331         956 :                 fDx += (sx != fSx);
    2332         956 :                 fDy += (sy != fSy);
    2333         956 :                 fSx = sx;
    2334         956 :                 fSy = sy;
    2335             : 
    2336         956 :                 if (fDx > 3 || fDy > 3) {
    2337           1 :                     fConvexity = SkPath::kConcave_Convexity;
    2338             :                 }
    2339             :             }
    2340             :         }
    2341             :     }
    2342             : 
    2343          91 :     void close() {
    2344          91 :         if (fPtCount > 2) {
    2345          91 :             this->addVec(fFirstVec);
    2346             :         }
    2347          91 :     }
    2348             : 
    2349         934 :     DirChange directionChange(const SkVector& curVec) {
    2350         934 :         SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
    2351             : 
    2352         934 :         SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
    2353         934 :         SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
    2354         934 :         largest = SkTMax(largest, -smallest);
    2355             : 
    2356         934 :         if (!almost_equal(largest, largest + cross)) {
    2357         619 :             int sign = SkScalarSignAsInt(cross);
    2358         619 :             if (sign) {
    2359         619 :                 return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
    2360             :             }
    2361             :         }
    2362             : 
    2363         315 :         if (cross) {
    2364           0 :             double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
    2365           0 :             double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
    2366           0 :             double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
    2367           0 :             double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
    2368           0 :             double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
    2369           0 :             if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
    2370           0 :                 int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
    2371           0 :                 if (sign) {
    2372           0 :                     return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
    2373             :                 }
    2374             :             }
    2375             :         }
    2376             : 
    2377         945 :         if (!SkScalarNearlyZero(fLastVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
    2378         630 :             !SkScalarNearlyZero(curVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
    2379         315 :             fLastVec.dot(curVec) < 0.0f) {
    2380           0 :             return kBackwards_DirChange;
    2381             :         }
    2382             : 
    2383         315 :         return kStraight_DirChange;
    2384             :     }
    2385             : 
    2386             : 
    2387         759 :     bool isFinite() const {
    2388         759 :         return fIsFinite;
    2389             :     }
    2390             : 
    2391         759 :     void setCurve(bool isCurve) {
    2392         759 :         fIsCurve = isCurve;
    2393         759 :     }
    2394             : 
    2395             : private:
    2396         934 :     void addVec(const SkVector& vec) {
    2397         934 :         SkASSERT(vec.fX || vec.fY);
    2398         934 :         DirChange dir = this->directionChange(vec);
    2399         934 :         switch (dir) {
    2400             :             case kLeft_DirChange:       // fall through
    2401             :             case kRight_DirChange:
    2402         619 :                 if (kInvalid_DirChange == fExpectedDir) {
    2403         113 :                     fExpectedDir = dir;
    2404         113 :                     fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
    2405             :                                                                 : SkPathPriv::kCCW_FirstDirection;
    2406         506 :                 } else if (dir != fExpectedDir) {
    2407          22 :                     fConvexity = SkPath::kConcave_Convexity;
    2408          22 :                     fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
    2409             :                 }
    2410         619 :                 fLastVec = vec;
    2411         619 :                 break;
    2412             :             case kStraight_DirChange:
    2413         315 :                 break;
    2414             :             case kBackwards_DirChange:
    2415           0 :                 if (fIsCurve) {
    2416             :                     // If any of the subsequent dir is non-backward, it'll be concave.
    2417             :                     // Otherwise, it's still convex.
    2418           0 :                     fExpectedDir = dir;
    2419             :                 }
    2420           0 :                 fLastVec = vec;
    2421           0 :                 break;
    2422             :             case kInvalid_DirChange:
    2423           0 :                 SkFAIL("Use of invalid direction change flag");
    2424           0 :                 break;
    2425             :         }
    2426         934 :     }
    2427             : 
    2428             :     SkPoint             fPriorPt;
    2429             :     SkPoint             fLastPt;
    2430             :     SkPoint             fCurrPt;
    2431             :     // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
    2432             :     // value with the current vec is deemed to be of a significant value.
    2433             :     SkVector            fLastVec, fFirstVec;
    2434             :     int                 fPtCount;   // non-degenerate points
    2435             :     DirChange           fExpectedDir;
    2436             :     SkPath::Convexity   fConvexity;
    2437             :     SkPathPriv::FirstDirection   fFirstDirection;
    2438             :     int                 fDx, fDy, fSx, fSy;
    2439             :     bool                fIsFinite;
    2440             :     bool                fIsCurve;
    2441             : };
    2442             : 
    2443         113 : SkPath::Convexity SkPath::internalGetConvexity() const {
    2444         113 :     SkASSERT(kUnknown_Convexity == fConvexity);
    2445             :     SkPoint         pts[4];
    2446             :     SkPath::Verb    verb;
    2447         113 :     SkPath::Iter    iter(*this, true);
    2448             : 
    2449         113 :     int             contourCount = 0;
    2450             :     int             count;
    2451         113 :     Convexicator    state;
    2452             : 
    2453         113 :     if (!isFinite()) {
    2454           0 :         return kUnknown_Convexity;
    2455             :     }
    2456        1587 :     while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) {
    2457         809 :         switch (verb) {
    2458             :             case kMove_Verb:
    2459         163 :                 if (++contourCount > 1) {
    2460          50 :                     fConvexity = kConcave_Convexity;
    2461          50 :                     return kConcave_Convexity;
    2462             :                 }
    2463         113 :                 pts[1] = pts[0];
    2464             :                 // fall through
    2465             :             case kLine_Verb:
    2466         461 :                 count = 1;
    2467         461 :                 state.setCurve(false);
    2468         461 :                 break;
    2469             :             case kQuad_Verb:
    2470             :                 // fall through
    2471             :             case kConic_Verb:
    2472             :                 // fall through
    2473             :             case kCubic_Verb:
    2474         207 :                 count = 2 + (kCubic_Verb == verb);
    2475             :                 // As an additional enhancement, this could set curve true only
    2476             :                 // if the curve is nonlinear
    2477         207 :                 state.setCurve(true);
    2478         207 :                 break;
    2479             :             case kClose_Verb:
    2480          91 :                 state.setCurve(false);
    2481          91 :                 state.close();
    2482          91 :                 count = 0;
    2483          91 :                 break;
    2484             :             default:
    2485           0 :                 SkDEBUGFAIL("bad verb");
    2486           0 :                 fConvexity = kConcave_Convexity;
    2487           0 :                 return kConcave_Convexity;
    2488             :         }
    2489             : 
    2490        1833 :         for (int i = 1; i <= count; i++) {
    2491        1074 :             state.addPt(pts[i]);
    2492             :         }
    2493             :         // early exit
    2494         759 :         if (!state.isFinite()) {
    2495           0 :             return kUnknown_Convexity;
    2496             :         }
    2497         759 :         if (kConcave_Convexity == state.getConvexity()) {
    2498          22 :             fConvexity = kConcave_Convexity;
    2499          22 :             return kConcave_Convexity;
    2500             :         }
    2501             :     }
    2502          41 :     fConvexity = state.getConvexity();
    2503          41 :     if (kConvex_Convexity == fConvexity && SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
    2504          41 :         fFirstDirection = state.getFirstDirection();
    2505             :     }
    2506          41 :     return static_cast<Convexity>(fConvexity);
    2507             : }
    2508             : 
    2509             : ///////////////////////////////////////////////////////////////////////////////
    2510             : 
    2511             : class ContourIter {
    2512             : public:
    2513             :     ContourIter(const SkPathRef& pathRef);
    2514             : 
    2515           0 :     bool done() const { return fDone; }
    2516             :     // if !done() then these may be called
    2517           0 :     int count() const { return fCurrPtCount; }
    2518           0 :     const SkPoint* pts() const { return fCurrPt; }
    2519             :     void next();
    2520             : 
    2521             : private:
    2522             :     int fCurrPtCount;
    2523             :     const SkPoint* fCurrPt;
    2524             :     const uint8_t* fCurrVerb;
    2525             :     const uint8_t* fStopVerbs;
    2526             :     const SkScalar* fCurrConicWeight;
    2527             :     bool fDone;
    2528             :     SkDEBUGCODE(int fContourCounter;)
    2529             : };
    2530             : 
    2531           0 : ContourIter::ContourIter(const SkPathRef& pathRef) {
    2532           0 :     fStopVerbs = pathRef.verbsMemBegin();
    2533           0 :     fDone = false;
    2534           0 :     fCurrPt = pathRef.points();
    2535           0 :     fCurrVerb = pathRef.verbs();
    2536           0 :     fCurrConicWeight = pathRef.conicWeights();
    2537           0 :     fCurrPtCount = 0;
    2538           0 :     SkDEBUGCODE(fContourCounter = 0;)
    2539           0 :     this->next();
    2540           0 : }
    2541             : 
    2542           0 : void ContourIter::next() {
    2543           0 :     if (fCurrVerb <= fStopVerbs) {
    2544           0 :         fDone = true;
    2545             :     }
    2546           0 :     if (fDone) {
    2547           0 :         return;
    2548             :     }
    2549             : 
    2550             :     // skip pts of prev contour
    2551           0 :     fCurrPt += fCurrPtCount;
    2552             : 
    2553           0 :     SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
    2554           0 :     int ptCount = 1;    // moveTo
    2555           0 :     const uint8_t* verbs = fCurrVerb;
    2556             : 
    2557           0 :     for (--verbs; verbs > fStopVerbs; --verbs) {
    2558           0 :         switch (verbs[~0]) {
    2559             :             case SkPath::kMove_Verb:
    2560           0 :                 goto CONTOUR_END;
    2561             :             case SkPath::kLine_Verb:
    2562           0 :                 ptCount += 1;
    2563           0 :                 break;
    2564             :             case SkPath::kConic_Verb:
    2565           0 :                 fCurrConicWeight += 1;
    2566             :                 // fall-through
    2567             :             case SkPath::kQuad_Verb:
    2568           0 :                 ptCount += 2;
    2569           0 :                 break;
    2570             :             case SkPath::kCubic_Verb:
    2571           0 :                 ptCount += 3;
    2572           0 :                 break;
    2573             :             case SkPath::kClose_Verb:
    2574           0 :                 break;
    2575             :             default:
    2576           0 :                 SkDEBUGFAIL("unexpected verb");
    2577           0 :                 break;
    2578             :         }
    2579             :     }
    2580             : CONTOUR_END:
    2581           0 :     fCurrPtCount = ptCount;
    2582           0 :     fCurrVerb = verbs;
    2583           0 :     SkDEBUGCODE(++fContourCounter;)
    2584             : }
    2585             : 
    2586             : // returns cross product of (p1 - p0) and (p2 - p0)
    2587           0 : static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
    2588           0 :     SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
    2589             :     // We may get 0 when the above subtracts underflow. We expect this to be
    2590             :     // very rare and lazily promote to double.
    2591           0 :     if (0 == cross) {
    2592           0 :         double p0x = SkScalarToDouble(p0.fX);
    2593           0 :         double p0y = SkScalarToDouble(p0.fY);
    2594             : 
    2595           0 :         double p1x = SkScalarToDouble(p1.fX);
    2596           0 :         double p1y = SkScalarToDouble(p1.fY);
    2597             : 
    2598           0 :         double p2x = SkScalarToDouble(p2.fX);
    2599           0 :         double p2y = SkScalarToDouble(p2.fY);
    2600             : 
    2601           0 :         cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
    2602             :                                  (p1y - p0y) * (p2x - p0x));
    2603             : 
    2604             :     }
    2605           0 :     return cross;
    2606             : }
    2607             : 
    2608             : // Returns the first pt with the maximum Y coordinate
    2609           0 : static int find_max_y(const SkPoint pts[], int count) {
    2610           0 :     SkASSERT(count > 0);
    2611           0 :     SkScalar max = pts[0].fY;
    2612           0 :     int firstIndex = 0;
    2613           0 :     for (int i = 1; i < count; ++i) {
    2614           0 :         SkScalar y = pts[i].fY;
    2615           0 :         if (y > max) {
    2616           0 :             max = y;
    2617           0 :             firstIndex = i;
    2618             :         }
    2619             :     }
    2620           0 :     return firstIndex;
    2621             : }
    2622             : 
    2623           0 : static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
    2624           0 :     int i = index;
    2625             :     for (;;) {
    2626           0 :         i = (i + inc) % n;
    2627           0 :         if (i == index) {   // we wrapped around, so abort
    2628           0 :             break;
    2629             :         }
    2630           0 :         if (pts[index] != pts[i]) { // found a different point, success!
    2631           0 :             break;
    2632             :         }
    2633             :     }
    2634           0 :     return i;
    2635             : }
    2636             : 
    2637             : /**
    2638             :  *  Starting at index, and moving forward (incrementing), find the xmin and
    2639             :  *  xmax of the contiguous points that have the same Y.
    2640             :  */
    2641           0 : static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
    2642             :                                int* maxIndexPtr) {
    2643           0 :     const SkScalar y = pts[index].fY;
    2644           0 :     SkScalar min = pts[index].fX;
    2645           0 :     SkScalar max = min;
    2646           0 :     int minIndex = index;
    2647           0 :     int maxIndex = index;
    2648           0 :     for (int i = index + 1; i < n; ++i) {
    2649           0 :         if (pts[i].fY != y) {
    2650           0 :             break;
    2651             :         }
    2652           0 :         SkScalar x = pts[i].fX;
    2653           0 :         if (x < min) {
    2654           0 :             min = x;
    2655           0 :             minIndex = i;
    2656           0 :         } else if (x > max) {
    2657           0 :             max = x;
    2658           0 :             maxIndex = i;
    2659             :         }
    2660             :     }
    2661           0 :     *maxIndexPtr = maxIndex;
    2662           0 :     return minIndex;
    2663             : }
    2664             : 
    2665           0 : static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
    2666           0 :     *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
    2667           0 : }
    2668             : 
    2669             : /*
    2670             :  *  We loop through all contours, and keep the computed cross-product of the
    2671             :  *  contour that contained the global y-max. If we just look at the first
    2672             :  *  contour, we may find one that is wound the opposite way (correctly) since
    2673             :  *  it is the interior of a hole (e.g. 'o'). Thus we must find the contour
    2674             :  *  that is outer most (or at least has the global y-max) before we can consider
    2675             :  *  its cross product.
    2676             :  */
    2677           0 : bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
    2678           0 :     if (kUnknown_FirstDirection != path.fFirstDirection.load()) {
    2679           0 :         *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
    2680           0 :         return true;
    2681             :     }
    2682             : 
    2683             :     // don't want to pay the cost for computing this if it
    2684             :     // is unknown, so we don't call isConvex()
    2685           0 :     if (SkPath::kConvex_Convexity == path.getConvexityOrUnknown()) {
    2686           0 :         SkASSERT(kUnknown_FirstDirection == path.fFirstDirection);
    2687           0 :         *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
    2688           0 :         return false;
    2689             :     }
    2690             : 
    2691           0 :     ContourIter iter(*path.fPathRef.get());
    2692             : 
    2693             :     // initialize with our logical y-min
    2694           0 :     SkScalar ymax = path.getBounds().fTop;
    2695           0 :     SkScalar ymaxCross = 0;
    2696             : 
    2697           0 :     for (; !iter.done(); iter.next()) {
    2698           0 :         int n = iter.count();
    2699           0 :         if (n < 3) {
    2700           0 :             continue;
    2701             :         }
    2702             : 
    2703           0 :         const SkPoint* pts = iter.pts();
    2704           0 :         SkScalar cross = 0;
    2705           0 :         int index = find_max_y(pts, n);
    2706           0 :         if (pts[index].fY < ymax) {
    2707           0 :             continue;
    2708             :         }
    2709             : 
    2710             :         // If there is more than 1 distinct point at the y-max, we take the
    2711             :         // x-min and x-max of them and just subtract to compute the dir.
    2712           0 :         if (pts[(index + 1) % n].fY == pts[index].fY) {
    2713             :             int maxIndex;
    2714           0 :             int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
    2715           0 :             if (minIndex == maxIndex) {
    2716           0 :                 goto TRY_CROSSPROD;
    2717             :             }
    2718           0 :             SkASSERT(pts[minIndex].fY == pts[index].fY);
    2719           0 :             SkASSERT(pts[maxIndex].fY == pts[index].fY);
    2720           0 :             SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
    2721             :             // we just subtract the indices, and let that auto-convert to
    2722             :             // SkScalar, since we just want - or + to signal the direction.
    2723           0 :             cross = minIndex - maxIndex;
    2724             :         } else {
    2725             :             TRY_CROSSPROD:
    2726             :             // Find a next and prev index to use for the cross-product test,
    2727             :             // but we try to find pts that form non-zero vectors from pts[index]
    2728             :             //
    2729             :             // Its possible that we can't find two non-degenerate vectors, so
    2730             :             // we have to guard our search (e.g. all the pts could be in the
    2731             :             // same place).
    2732             : 
    2733             :             // we pass n - 1 instead of -1 so we don't foul up % operator by
    2734             :             // passing it a negative LH argument.
    2735           0 :             int prev = find_diff_pt(pts, index, n, n - 1);
    2736           0 :             if (prev == index) {
    2737             :                 // completely degenerate, skip to next contour
    2738           0 :                 continue;
    2739             :             }
    2740           0 :             int next = find_diff_pt(pts, index, n, 1);
    2741           0 :             SkASSERT(next != index);
    2742           0 :             cross = cross_prod(pts[prev], pts[index], pts[next]);
    2743             :             // if we get a zero and the points are horizontal, then we look at the spread in
    2744             :             // x-direction. We really should continue to walk away from the degeneracy until
    2745             :             // there is a divergence.
    2746           0 :             if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
    2747             :                 // construct the subtract so we get the correct Direction below
    2748           0 :                 cross = pts[index].fX - pts[next].fX;
    2749             :             }
    2750             :         }
    2751             : 
    2752           0 :         if (cross) {
    2753             :             // record our best guess so far
    2754           0 :             ymax = pts[index].fY;
    2755           0 :             ymaxCross = cross;
    2756             :         }
    2757             :     }
    2758           0 :     if (ymaxCross) {
    2759           0 :         crossToDir(ymaxCross, dir);
    2760           0 :         path.fFirstDirection = *dir;
    2761           0 :         return true;
    2762             :     } else {
    2763           0 :         return false;
    2764             :     }
    2765             : }
    2766             : 
    2767             : ///////////////////////////////////////////////////////////////////////////////
    2768             : 
    2769           0 : static bool between(SkScalar a, SkScalar b, SkScalar c) {
    2770           0 :     SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
    2771             :             || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
    2772           0 :     return (a - b) * (c - b) <= 0;
    2773             : }
    2774             : 
    2775           0 : static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
    2776             :                                SkScalar t) {
    2777           0 :     SkScalar A = c3 + 3*(c1 - c2) - c0;
    2778           0 :     SkScalar B = 3*(c2 - c1 - c1 + c0);
    2779           0 :     SkScalar C = 3*(c1 - c0);
    2780           0 :     SkScalar D = c0;
    2781           0 :     return poly_eval(A, B, C, D, t);
    2782             : }
    2783             : 
    2784           0 : template <size_t N> static void find_minmax(const SkPoint pts[],
    2785             :                                             SkScalar* minPtr, SkScalar* maxPtr) {
    2786             :     SkScalar min, max;
    2787           0 :     min = max = pts[0].fX;
    2788           0 :     for (size_t i = 1; i < N; ++i) {
    2789           0 :         min = SkMinScalar(min, pts[i].fX);
    2790           0 :         max = SkMaxScalar(max, pts[i].fX);
    2791             :     }
    2792           0 :     *minPtr = min;
    2793           0 :     *maxPtr = max;
    2794           0 : }
    2795             : 
    2796           0 : static bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
    2797           0 :     if (start.fY == end.fY) {
    2798           0 :         return between(start.fX, x, end.fX) && x != end.fX;
    2799             :     } else {
    2800           0 :         return x == start.fX && y == start.fY;
    2801             :     }
    2802             : }
    2803             : 
    2804           0 : static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
    2805           0 :     SkScalar y0 = pts[0].fY;
    2806           0 :     SkScalar y3 = pts[3].fY;
    2807             : 
    2808           0 :     int dir = 1;
    2809           0 :     if (y0 > y3) {
    2810           0 :         SkTSwap(y0, y3);
    2811           0 :         dir = -1;
    2812             :     }
    2813           0 :     if (y < y0 || y > y3) {
    2814           0 :         return 0;
    2815             :     }
    2816           0 :     if (checkOnCurve(x, y, pts[0], pts[3])) {
    2817           0 :         *onCurveCount += 1;
    2818           0 :         return 0;
    2819             :     }
    2820           0 :     if (y == y3) {
    2821           0 :         return 0;
    2822             :     }
    2823             : 
    2824             :     // quickreject or quickaccept
    2825             :     SkScalar min, max;
    2826           0 :     find_minmax<4>(pts, &min, &max);
    2827           0 :     if (x < min) {
    2828           0 :         return 0;
    2829             :     }
    2830           0 :     if (x > max) {
    2831           0 :         return dir;
    2832             :     }
    2833             : 
    2834             :     // compute the actual x(t) value
    2835             :     SkScalar t;
    2836           0 :     if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
    2837           0 :         return 0;
    2838             :     }
    2839           0 :     SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
    2840           0 :     if (SkScalarNearlyEqual(xt, x)) {
    2841           0 :         if (x != pts[3].fX || y != pts[3].fY) {  // don't test end points; they're start points
    2842           0 :             *onCurveCount += 1;
    2843           0 :             return 0;
    2844             :         }
    2845             :     }
    2846           0 :     return xt < x ? dir : 0;
    2847             : }
    2848             : 
    2849           0 : static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
    2850             :     SkPoint dst[10];
    2851           0 :     int n = SkChopCubicAtYExtrema(pts, dst);
    2852           0 :     int w = 0;
    2853           0 :     for (int i = 0; i <= n; ++i) {
    2854           0 :         w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
    2855             :     }
    2856           0 :     return w;
    2857             : }
    2858             : 
    2859           0 : static double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
    2860           0 :     SkASSERT(src);
    2861           0 :     SkASSERT(t >= 0 && t <= 1);
    2862           0 :     SkScalar src2w = src[2] * w;
    2863           0 :     SkScalar C = src[0];
    2864           0 :     SkScalar A = src[4] - 2 * src2w + C;
    2865           0 :     SkScalar B = 2 * (src2w - C);
    2866           0 :     return poly_eval(A, B, C, t);
    2867             : }
    2868             : 
    2869             : 
    2870           0 : static double conic_eval_denominator(SkScalar w, SkScalar t) {
    2871           0 :     SkScalar B = 2 * (w - 1);
    2872           0 :     SkScalar C = 1;
    2873           0 :     SkScalar A = -B;
    2874           0 :     return poly_eval(A, B, C, t);
    2875             : }
    2876             : 
    2877           0 : static int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
    2878           0 :     const SkPoint* pts = conic.fPts;
    2879           0 :     SkScalar y0 = pts[0].fY;
    2880           0 :     SkScalar y2 = pts[2].fY;
    2881             : 
    2882           0 :     int dir = 1;
    2883           0 :     if (y0 > y2) {
    2884           0 :         SkTSwap(y0, y2);
    2885           0 :         dir = -1;
    2886             :     }
    2887           0 :     if (y < y0 || y > y2) {
    2888           0 :         return 0;
    2889             :     }
    2890           0 :     if (checkOnCurve(x, y, pts[0], pts[2])) {
    2891           0 :         *onCurveCount += 1;
    2892           0 :         return 0;
    2893             :     }
    2894           0 :     if (y == y2) {
    2895           0 :         return 0;
    2896             :     }
    2897             : 
    2898             :     SkScalar roots[2];
    2899           0 :     SkScalar A = pts[2].fY;
    2900           0 :     SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
    2901           0 :     SkScalar C = pts[0].fY;
    2902           0 :     A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
    2903           0 :     B -= C;  // B = b*w - w * yCept + yCept - a
    2904           0 :     C -= y;
    2905           0 :     int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
    2906           0 :     SkASSERT(n <= 1);
    2907             :     SkScalar xt;
    2908           0 :     if (0 == n) {
    2909             :         // zero roots are returned only when y0 == y
    2910             :         // Need [0] if dir == 1
    2911             :         // and  [2] if dir == -1
    2912           0 :         xt = pts[1 - dir].fX;
    2913             :     } else {
    2914           0 :         SkScalar t = roots[0];
    2915           0 :         xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
    2916             :     }
    2917           0 :     if (SkScalarNearlyEqual(xt, x)) {
    2918           0 :         if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
    2919           0 :             *onCurveCount += 1;
    2920           0 :             return 0;
    2921             :         }
    2922             :     }
    2923           0 :     return xt < x ? dir : 0;
    2924             : }
    2925             : 
    2926           0 : static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
    2927             :     //    return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
    2928           0 :     if (y0 == y1) {
    2929           0 :         return true;
    2930             :     }
    2931           0 :     if (y0 < y1) {
    2932           0 :         return y1 <= y2;
    2933             :     } else {
    2934           0 :         return y1 >= y2;
    2935             :     }
    2936             : }
    2937             : 
    2938           0 : static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
    2939             :                          int* onCurveCount) {
    2940           0 :     SkConic conic(pts, weight);
    2941           0 :     SkConic chopped[2];
    2942             :     // If the data points are very large, the conic may not be monotonic but may also
    2943             :     // fail to chop. Then, the chopper does not split the original conic in two.
    2944           0 :     bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
    2945           0 :     int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
    2946           0 :     if (!isMono) {
    2947           0 :         w += winding_mono_conic(chopped[1], x, y, onCurveCount);
    2948             :     }
    2949           0 :     return w;
    2950             : }
    2951             : 
    2952           0 : static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
    2953           0 :     SkScalar y0 = pts[0].fY;
    2954           0 :     SkScalar y2 = pts[2].fY;
    2955             : 
    2956           0 :     int dir = 1;
    2957           0 :     if (y0 > y2) {
    2958           0 :         SkTSwap(y0, y2);
    2959           0 :         dir = -1;
    2960             :     }
    2961           0 :     if (y < y0 || y > y2) {
    2962           0 :         return 0;
    2963             :     }
    2964           0 :     if (checkOnCurve(x, y, pts[0], pts[2])) {
    2965           0 :         *onCurveCount += 1;
    2966           0 :         return 0;
    2967             :     }
    2968           0 :     if (y == y2) {
    2969           0 :         return 0;
    2970             :     }
    2971             :     // bounds check on X (not required. is it faster?)
    2972             : #if 0
    2973             :     if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
    2974             :         return 0;
    2975             :     }
    2976             : #endif
    2977             : 
    2978             :     SkScalar roots[2];
    2979           0 :     int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
    2980           0 :                                 2 * (pts[1].fY - pts[0].fY),
    2981           0 :                                 pts[0].fY - y,
    2982           0 :                                 roots);
    2983           0 :     SkASSERT(n <= 1);
    2984             :     SkScalar xt;
    2985           0 :     if (0 == n) {
    2986             :         // zero roots are returned only when y0 == y
    2987             :         // Need [0] if dir == 1
    2988             :         // and  [2] if dir == -1
    2989           0 :         xt = pts[1 - dir].fX;
    2990             :     } else {
    2991           0 :         SkScalar t = roots[0];
    2992           0 :         SkScalar C = pts[0].fX;
    2993           0 :         SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
    2994           0 :         SkScalar B = 2 * (pts[1].fX - C);
    2995           0 :         xt = poly_eval(A, B, C, t);
    2996             :     }
    2997           0 :     if (SkScalarNearlyEqual(xt, x)) {
    2998           0 :         if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
    2999           0 :             *onCurveCount += 1;
    3000           0 :             return 0;
    3001             :         }
    3002             :     }
    3003           0 :     return xt < x ? dir : 0;
    3004             : }
    3005             : 
    3006           0 : static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
    3007             :     SkPoint dst[5];
    3008           0 :     int     n = 0;
    3009             : 
    3010           0 :     if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
    3011           0 :         n = SkChopQuadAtYExtrema(pts, dst);
    3012           0 :         pts = dst;
    3013             :     }
    3014           0 :     int w = winding_mono_quad(pts, x, y, onCurveCount);
    3015           0 :     if (n > 0) {
    3016           0 :         w += winding_mono_quad(&pts[2], x, y, onCurveCount);
    3017             :     }
    3018           0 :     return w;
    3019             : }
    3020             : 
    3021           0 : static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
    3022           0 :     SkScalar x0 = pts[0].fX;
    3023           0 :     SkScalar y0 = pts[0].fY;
    3024           0 :     SkScalar x1 = pts[1].fX;
    3025           0 :     SkScalar y1 = pts[1].fY;
    3026             : 
    3027           0 :     SkScalar dy = y1 - y0;
    3028             : 
    3029           0 :     int dir = 1;
    3030           0 :     if (y0 > y1) {
    3031           0 :         SkTSwap(y0, y1);
    3032           0 :         dir = -1;
    3033             :     }
    3034           0 :     if (y < y0 || y > y1) {
    3035           0 :         return 0;
    3036             :     }
    3037           0 :     if (checkOnCurve(x, y, pts[0], pts[1])) {
    3038           0 :         *onCurveCount += 1;
    3039           0 :         return 0;
    3040             :     }
    3041           0 :     if (y == y1) {
    3042           0 :         return 0;
    3043             :     }
    3044           0 :     SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0);
    3045             : 
    3046           0 :     if (!cross) {
    3047             :         // zero cross means the point is on the line, and since the case where
    3048             :         // y of the query point is at the end point is handled above, we can be
    3049             :         // sure that we're on the line (excluding the end point) here
    3050           0 :         if (x != x1 || y != pts[1].fY) {
    3051           0 :             *onCurveCount += 1;
    3052             :         }
    3053           0 :         dir = 0;
    3054           0 :     } else if (SkScalarSignAsInt(cross) == dir) {
    3055           0 :         dir = 0;
    3056             :     }
    3057           0 :     return dir;
    3058             : }
    3059             : 
    3060           0 : static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
    3061             :         SkTDArray<SkVector>* tangents) {
    3062           0 :     if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
    3063           0 :              && !between(pts[2].fY, y, pts[3].fY)) {
    3064           0 :         return;
    3065             :     }
    3066           0 :     if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
    3067           0 :              && !between(pts[2].fX, x, pts[3].fX)) {
    3068           0 :         return;
    3069             :     }
    3070             :     SkPoint dst[10];
    3071           0 :     int n = SkChopCubicAtYExtrema(pts, dst);
    3072           0 :     for (int i = 0; i <= n; ++i) {
    3073           0 :         SkPoint* c = &dst[i * 3];
    3074             :         SkScalar t;
    3075           0 :         if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
    3076           0 :             continue;
    3077             :         }
    3078           0 :         SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
    3079           0 :         if (!SkScalarNearlyEqual(x, xt)) {
    3080           0 :             continue;
    3081             :         }
    3082             :         SkVector tangent;
    3083           0 :         SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
    3084           0 :         tangents->push(tangent);
    3085             :     }
    3086             : }
    3087             : 
    3088           0 : static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
    3089             :             SkTDArray<SkVector>* tangents) {
    3090           0 :     if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
    3091           0 :         return;
    3092             :     }
    3093           0 :     if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
    3094           0 :         return;
    3095             :     }
    3096             :     SkScalar roots[2];
    3097           0 :     SkScalar A = pts[2].fY;
    3098           0 :     SkScalar B = pts[1].fY * w - y * w + y;
    3099           0 :     SkScalar C = pts[0].fY;
    3100           0 :     A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
    3101           0 :     B -= C;  // B = b*w - w * yCept + yCept - a
    3102           0 :     C -= y;
    3103           0 :     int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
    3104           0 :     for (int index = 0; index < n; ++index) {
    3105           0 :         SkScalar t = roots[index];
    3106           0 :         SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
    3107           0 :         if (!SkScalarNearlyEqual(x, xt)) {
    3108           0 :             continue;
    3109             :         }
    3110           0 :         SkConic conic(pts, w);
    3111           0 :         tangents->push(conic.evalTangentAt(t));
    3112             :     }
    3113             : }
    3114             : 
    3115           0 : static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
    3116             :         SkTDArray<SkVector>* tangents) {
    3117           0 :     if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
    3118           0 :         return;
    3119             :     }
    3120           0 :     if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
    3121           0 :         return;
    3122             :     }
    3123             :     SkScalar roots[2];
    3124           0 :     int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
    3125           0 :                                 2 * (pts[1].fY - pts[0].fY),
    3126           0 :                                 pts[0].fY - y,
    3127           0 :                                 roots);
    3128           0 :     for (int index = 0; index < n; ++index) {
    3129           0 :         SkScalar t = roots[index];
    3130           0 :         SkScalar C = pts[0].fX;
    3131           0 :         SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
    3132           0 :         SkScalar B = 2 * (pts[1].fX - C);
    3133           0 :         SkScalar xt = poly_eval(A, B, C, t);
    3134           0 :         if (!SkScalarNearlyEqual(x, xt)) {
    3135           0 :             continue;
    3136             :         }
    3137           0 :         tangents->push(SkEvalQuadTangentAt(pts, t));
    3138             :     }
    3139             : }
    3140             : 
    3141           0 : static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
    3142             :         SkTDArray<SkVector>* tangents) {
    3143           0 :     SkScalar y0 = pts[0].fY;
    3144           0 :     SkScalar y1 = pts[1].fY;
    3145           0 :     if (!between(y0, y, y1)) {
    3146           0 :         return;
    3147             :     }
    3148           0 :     SkScalar x0 = pts[0].fX;
    3149           0 :     SkScalar x1 = pts[1].fX;
    3150           0 :     if (!between(x0, x, x1)) {
    3151           0 :         return;
    3152             :     }
    3153           0 :     SkScalar dx = x1 - x0;
    3154           0 :     SkScalar dy = y1 - y0;
    3155           0 :     if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
    3156           0 :         return;
    3157             :     }
    3158             :     SkVector v;
    3159           0 :     v.set(dx, dy);
    3160           0 :     tangents->push(v);
    3161             : }
    3162             : 
    3163           0 : static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
    3164           0 :     return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
    3165             : }
    3166             : 
    3167           0 : bool SkPath::contains(SkScalar x, SkScalar y) const {
    3168           0 :     bool isInverse = this->isInverseFillType();
    3169           0 :     if (this->isEmpty()) {
    3170           0 :         return isInverse;
    3171             :     }
    3172             : 
    3173           0 :     if (!contains_inclusive(this->getBounds(), x, y)) {
    3174           0 :         return isInverse;
    3175             :     }
    3176             : 
    3177           0 :     SkPath::Iter iter(*this, true);
    3178           0 :     bool done = false;
    3179           0 :     int w = 0;
    3180           0 :     int onCurveCount = 0;
    3181           0 :     do {
    3182             :         SkPoint pts[4];
    3183           0 :         switch (iter.next(pts, false)) {
    3184             :             case SkPath::kMove_Verb:
    3185             :             case SkPath::kClose_Verb:
    3186           0 :                 break;
    3187             :             case SkPath::kLine_Verb:
    3188           0 :                 w += winding_line(pts, x, y, &onCurveCount);
    3189           0 :                 break;
    3190             :             case SkPath::kQuad_Verb:
    3191           0 :                 w += winding_quad(pts, x, y, &onCurveCount);
    3192           0 :                 break;
    3193             :             case SkPath::kConic_Verb:
    3194           0 :                 w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
    3195           0 :                 break;
    3196             :             case SkPath::kCubic_Verb:
    3197           0 :                 w += winding_cubic(pts, x, y, &onCurveCount);
    3198           0 :                 break;
    3199             :             case SkPath::kDone_Verb:
    3200           0 :                 done = true;
    3201           0 :                 break;
    3202             :        }
    3203           0 :     } while (!done);
    3204           0 :     bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
    3205           0 :             || SkPath::kInverseEvenOdd_FillType == this->getFillType();
    3206           0 :     if (evenOddFill) {
    3207           0 :         w &= 1;
    3208             :     }
    3209           0 :     if (w) {
    3210           0 :         return !isInverse;
    3211             :     }
    3212           0 :     if (onCurveCount <= 1) {
    3213           0 :         return SkToBool(onCurveCount) ^ isInverse;
    3214             :     }
    3215           0 :     if ((onCurveCount & 1) || evenOddFill) {
    3216           0 :         return SkToBool(onCurveCount & 1) ^ isInverse;
    3217             :     }
    3218             :     // If the point touches an even number of curves, and the fill is winding, check for
    3219             :     // coincidence. Count coincidence as places where the on curve points have identical tangents.
    3220           0 :     iter.setPath(*this, true);
    3221           0 :     done = false;
    3222           0 :     SkTDArray<SkVector> tangents;
    3223           0 :     do {
    3224             :         SkPoint pts[4];
    3225           0 :         int oldCount = tangents.count();
    3226           0 :         switch (iter.next(pts, false)) {
    3227             :             case SkPath::kMove_Verb:
    3228             :             case SkPath::kClose_Verb:
    3229           0 :                 break;
    3230             :             case SkPath::kLine_Verb:
    3231           0 :                 tangent_line(pts, x, y, &tangents);
    3232           0 :                 break;
    3233             :             case SkPath::kQuad_Verb:
    3234           0 :                 tangent_quad(pts, x, y, &tangents);
    3235           0 :                 break;
    3236             :             case SkPath::kConic_Verb:
    3237           0 :                 tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
    3238           0 :                 break;
    3239             :             case SkPath::kCubic_Verb:
    3240           0 :                 tangent_cubic(pts, x, y, &tangents);
    3241           0 :                 break;
    3242             :             case SkPath::kDone_Verb:
    3243           0 :                 done = true;
    3244           0 :                 break;
    3245             :        }
    3246           0 :        if (tangents.count() > oldCount) {
    3247           0 :             int last = tangents.count() - 1;
    3248           0 :             const SkVector& tangent = tangents[last];
    3249           0 :             if (SkScalarNearlyZero(tangent.lengthSqd())) {
    3250           0 :                 tangents.remove(last);
    3251             :             } else {
    3252           0 :                 for (int index = 0; index < last; ++index) {
    3253           0 :                     const SkVector& test = tangents[index];
    3254           0 :                     if (SkScalarNearlyZero(test.cross(tangent))
    3255           0 :                             && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
    3256           0 :                             && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
    3257           0 :                         tangents.remove(last);
    3258           0 :                         tangents.removeShuffle(index);
    3259           0 :                         break;
    3260             :                     }
    3261             :                 }
    3262             :             }
    3263             :         }
    3264           0 :     } while (!done);
    3265           0 :     return SkToBool(tangents.count()) ^ isInverse;
    3266             : }
    3267             : 
    3268           0 : int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
    3269             :                                 SkScalar w, SkPoint pts[], int pow2) {
    3270           0 :     const SkConic conic(p0, p1, p2, w);
    3271           0 :     return conic.chopIntoQuadsPOW2(pts, pow2);
    3272             : }
    3273             : 
    3274           0 : bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
    3275             :                                     unsigned* start) {
    3276           0 :     if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
    3277           0 :         return false;
    3278             :     }
    3279           0 :     SkPath::RawIter iter(path);
    3280             :     SkPoint verbPts[4];
    3281             :     SkPath::Verb v;
    3282             :     SkPoint rectPts[5];
    3283           0 :     int rectPtCnt = 0;
    3284           0 :     while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) {
    3285           0 :         switch (v) {
    3286             :             case SkPath::kMove_Verb:
    3287           0 :                 if (0 != rectPtCnt) {
    3288           0 :                     return false;
    3289             :                 }
    3290           0 :                 rectPts[0] = verbPts[0];
    3291           0 :                 ++rectPtCnt;
    3292           0 :                 break;
    3293             :             case SkPath::kLine_Verb:
    3294           0 :                 if (5 == rectPtCnt) {
    3295           0 :                     return false;
    3296             :                 }
    3297           0 :                 rectPts[rectPtCnt] = verbPts[1];
    3298           0 :                 ++rectPtCnt;
    3299           0 :                 break;
    3300             :             case SkPath::kClose_Verb:
    3301           0 :                 if (4 == rectPtCnt) {
    3302           0 :                     rectPts[4] = rectPts[0];
    3303           0 :                     rectPtCnt = 5;
    3304             :                 }
    3305           0 :                 break;
    3306             :             default:
    3307           0 :                 return false;
    3308             :         }
    3309             :     }
    3310           0 :     if (rectPtCnt < 5) {
    3311           0 :         return false;
    3312             :     }
    3313           0 :     if (rectPts[0] != rectPts[4]) {
    3314           0 :         return false;
    3315             :     }
    3316             :     // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
    3317             :     // and pts 1 and 2 the opposite vertical or horizontal edge).
    3318             :     bool vec03IsVertical;
    3319           0 :     if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
    3320           0 :         rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
    3321             :         // Make sure it has non-zero width and height
    3322           0 :         if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
    3323           0 :             return false;
    3324             :         }
    3325           0 :         vec03IsVertical = true;
    3326           0 :     } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
    3327           0 :                rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
    3328             :         // Make sure it has non-zero width and height
    3329           0 :         if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
    3330           0 :             return false;
    3331             :         }
    3332           0 :         vec03IsVertical = false;
    3333             :     } else {
    3334           0 :         return false;
    3335             :     }
    3336             :     // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
    3337             :     // set if it is on the bottom edge.
    3338             :     unsigned sortFlags =
    3339           0 :             ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
    3340           0 :             ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
    3341           0 :     switch (sortFlags) {
    3342             :         case 0b00:
    3343           0 :             rect->set(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
    3344           0 :             *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
    3345           0 :             *start = 0;
    3346           0 :             break;
    3347             :         case 0b01:
    3348           0 :             rect->set(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
    3349           0 :             *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
    3350           0 :             *start = 1;
    3351           0 :             break;
    3352             :         case 0b10:
    3353           0 :             rect->set(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
    3354           0 :             *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
    3355           0 :             *start = 3;
    3356           0 :             break;
    3357             :         case 0b11:
    3358           0 :             rect->set(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
    3359           0 :             *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
    3360           0 :             *start = 2;
    3361           0 :             break;
    3362             :     }
    3363           0 :     return true;
    3364             : }
    3365             : 
    3366           0 : void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
    3367             :                                    SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
    3368           0 :     SkASSERT(!oval.isEmpty());
    3369           0 :     SkASSERT(sweepAngle);
    3370             : 
    3371           0 :     path->reset();
    3372           0 :     path->setIsVolatile(true);
    3373           0 :     path->setFillType(SkPath::kWinding_FillType);
    3374           0 :     if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
    3375           0 :         path->addOval(oval);
    3376           0 :         return;
    3377             :     }
    3378           0 :     if (useCenter) {
    3379           0 :         path->moveTo(oval.centerX(), oval.centerY());
    3380             :     }
    3381             :     // Arc to mods at 360 and drawArc is not supposed to.
    3382           0 :     bool forceMoveTo = !useCenter;
    3383           0 :     while (sweepAngle <= -360.f) {
    3384           0 :         path->arcTo(oval, startAngle, -180.f, forceMoveTo);
    3385           0 :         startAngle -= 180.f;
    3386           0 :         path->arcTo(oval, startAngle, -180.f, false);
    3387           0 :         startAngle -= 180.f;
    3388           0 :         forceMoveTo = false;
    3389           0 :         sweepAngle += 360.f;
    3390             :     }
    3391           0 :     while (sweepAngle >= 360.f) {
    3392           0 :         path->arcTo(oval, startAngle, 180.f, forceMoveTo);
    3393           0 :         startAngle += 180.f;
    3394           0 :         path->arcTo(oval, startAngle, 180.f, false);
    3395           0 :         startAngle += 180.f;
    3396           0 :         forceMoveTo = false;
    3397           0 :         sweepAngle -= 360.f;
    3398             :     }
    3399           0 :     path->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
    3400           0 :     if (useCenter) {
    3401           0 :         path->close();
    3402             :     }
    3403             : }
    3404             : 
    3405             : ///////////////////////////////////////////////////////////////////////////////////////////////////
    3406             : #include "SkNx.h"
    3407             : 
    3408           0 : static int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) {
    3409             :     SkScalar ts[2];
    3410           0 :     int n  = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts);
    3411           0 :         n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]);
    3412           0 :     SkASSERT(n >= 0 && n <= 2);
    3413           0 :     for (int i = 0; i < n; ++i) {
    3414           0 :         extremas[i] = SkEvalQuadAt(src, ts[i]);
    3415             :     }
    3416           0 :     extremas[n] = src[2];
    3417           0 :     return n + 1;
    3418             : }
    3419             : 
    3420           0 : static int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) {
    3421           0 :     SkConic conic(src[0], src[1], src[2], w);
    3422             :     SkScalar ts[2];
    3423           0 :     int n  = conic.findXExtrema(ts);
    3424           0 :         n += conic.findYExtrema(&ts[n]);
    3425           0 :     SkASSERT(n >= 0 && n <= 2);
    3426           0 :     for (int i = 0; i < n; ++i) {
    3427           0 :         extremas[i] = conic.evalAt(ts[i]);
    3428             :     }
    3429           0 :     extremas[n] = src[2];
    3430           0 :     return n + 1;
    3431             : }
    3432             : 
    3433         539 : static int compute_cubic_extremas(const SkPoint src[3], SkPoint extremas[5]) {
    3434             :     SkScalar ts[4];
    3435         539 :     int n  = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts);
    3436         539 :         n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]);
    3437         539 :     SkASSERT(n >= 0 && n <= 4);
    3438         781 :     for (int i = 0; i < n; ++i) {
    3439         242 :         SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr);
    3440             :     }
    3441         539 :     extremas[n] = src[3];
    3442         539 :     return n + 1;
    3443             : }
    3444             : 
    3445          98 : SkRect SkPath::computeTightBounds() const {
    3446          98 :     if (0 == this->countVerbs()) {
    3447           0 :         return SkRect::MakeEmpty();
    3448             :     }
    3449             : 
    3450          98 :     if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
    3451          56 :         return this->getBounds();
    3452             :     }
    3453             :     
    3454             :     SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1
    3455             :     SkPoint pts[4];
    3456          42 :     SkPath::RawIter iter(*this);
    3457             : 
    3458             :     // initial with the first MoveTo, so we don't have to check inside the switch
    3459             :     Sk2s min, max;
    3460          42 :     min = max = from_point(this->getPoint(0));
    3461             :     for (;;) {
    3462        1059 :         int count = 0;
    3463        1059 :         switch (iter.next(pts)) {
    3464             :             case SkPath::kMove_Verb:
    3465          86 :                 extremas[0] = pts[0];
    3466          86 :                 count = 1;
    3467          86 :                 break;
    3468             :             case SkPath::kLine_Verb:
    3469         308 :                 extremas[0] = pts[1];
    3470         308 :                 count = 1;
    3471         308 :                 break;
    3472             :             case SkPath::kQuad_Verb:
    3473           0 :                 count = compute_quad_extremas(pts, extremas);
    3474           0 :                 break;
    3475             :             case SkPath::kConic_Verb:
    3476           0 :                 count = compute_conic_extremas(pts, iter.conicWeight(), extremas);
    3477           0 :                 break;
    3478             :             case SkPath::kCubic_Verb:
    3479         539 :                 count = compute_cubic_extremas(pts, extremas);
    3480         539 :                 break;
    3481             :             case SkPath::kClose_Verb:
    3482          84 :                 break;
    3483             :             case SkPath::kDone_Verb:
    3484          42 :                 goto DONE;
    3485             :         }
    3486        2192 :         for (int i = 0; i < count; ++i) {
    3487        1175 :             Sk2s tmp = from_point(extremas[i]);
    3488        1175 :             min = Sk2s::Min(min, tmp);
    3489        1175 :             max = Sk2s::Max(max, tmp);
    3490             :         }
    3491        1017 :     }
    3492             : DONE:
    3493             :     SkRect bounds;
    3494             :     min.store((SkPoint*)&bounds.fLeft);
    3495             :     max.store((SkPoint*)&bounds.fRight);
    3496          42 :     return bounds;
    3497             : }

Generated by: LCOV version 1.13