LCOV - code coverage report
Current view: directory - gfx/skia/src/core - SkPath.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 949 0 0.0 %
Date: 2012-06-02 Functions: 76 0 0.0 %

       1                 : 
       2                 : /*
       3                 :  * Copyright 2006 The Android Open Source Project
       4                 :  *
       5                 :  * Use of this source code is governed by a BSD-style license that can be
       6                 :  * found in the LICENSE file.
       7                 :  */
       8                 : 
       9                 : 
      10                 : #include "SkPath.h"
      11                 : #include "SkReader32.h"
      12                 : #include "SkWriter32.h"
      13                 : #include "SkMath.h"
      14                 : 
      15                 : ////////////////////////////////////////////////////////////////////////////
      16                 : 
      17                 : /**
      18                 :  *  Path.bounds is defined to be the bounds of all the control points.
      19                 :  *  If we called bounds.join(r) we would skip r if r was empty, which breaks
      20                 :  *  our promise. Hence we have a custom joiner that doesn't look at emptiness
      21                 :  */
      22               0 : static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
      23               0 :     dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
      24               0 :     dst->fTop = SkMinScalar(dst->fTop, src.fTop);
      25               0 :     dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
      26               0 :     dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
      27               0 : }
      28                 : 
      29                 : /*  This guy's constructor/destructor bracket a path editing operation. It is
      30                 :     used when we know the bounds of the amount we are going to add to the path
      31                 :     (usually a new contour, but not required).
      32                 : 
      33                 :     It captures some state about the path up front (i.e. if it already has a
      34                 :     cached bounds), and the if it can, it updates the cache bounds explicitly,
      35                 :     avoiding the need to revisit all of the points in getBounds().
      36                 : 
      37                 :     It also notes if the path was originally empty, and if so, sets isConvex
      38                 :     to true. Thus it can only be used if the contour being added is convex.
      39                 :  */
      40                 : class SkAutoPathBoundsUpdate {
      41                 : public:
      42               0 :     SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
      43               0 :         this->init(path);
      44               0 :     }
      45                 : 
      46               0 :     SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
      47                 :                            SkScalar right, SkScalar bottom) {
      48               0 :         fRect.set(left, top, right, bottom);
      49               0 :         this->init(path);
      50               0 :     }
      51                 : 
      52               0 :     ~SkAutoPathBoundsUpdate() {
      53               0 :         fPath->setIsConvex(fEmpty);
      54               0 :         if (fEmpty) {
      55               0 :             fPath->fBounds = fRect;
      56               0 :             fPath->fBoundsIsDirty = false;
      57               0 :         } else if (!fDirty) {
      58               0 :             joinNoEmptyChecks(&fPath->fBounds, fRect);
      59               0 :             fPath->fBoundsIsDirty = false;
      60                 :         }
      61               0 :     }
      62                 : 
      63                 : private:
      64                 :     SkPath* fPath;
      65                 :     SkRect  fRect;
      66                 :     bool    fDirty;
      67                 :     bool    fEmpty;
      68                 : 
      69                 :     // returns true if we should proceed
      70               0 :     void init(SkPath* path) {
      71               0 :         fPath = path;
      72               0 :         fDirty = SkToBool(path->fBoundsIsDirty);
      73               0 :         fEmpty = path->isEmpty();
      74                 :         // Cannot use fRect for our bounds unless we know it is sorted
      75               0 :         fRect.sort();
      76               0 :     }
      77                 : };
      78                 : 
      79               0 : static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
      80               0 :     if (pts.count() <= 1) {  // we ignore just 1 point (moveto)
      81               0 :         bounds->set(0, 0, 0, 0);
      82                 :     } else {
      83               0 :         bounds->set(pts.begin(), pts.count());
      84                 : //        SkDebugf("------- compute bounds %p %d", &pts, pts.count());
      85                 :     }
      86               0 : }
      87                 : 
      88                 : ////////////////////////////////////////////////////////////////////////////
      89                 : 
      90                 : /*
      91                 :     Stores the verbs and points as they are given to us, with exceptions:
      92                 :     - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
      93                 :     - we insert a Move(0,0) if Line | Quad | Cubic is our first command
      94                 : 
      95                 :     The iterator does more cleanup, especially if forceClose == true
      96                 :     1. If we encounter degenerate segments, remove them
      97                 :     2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
      98                 :     3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
      99                 :     4. if we encounter Line | Quad | Cubic after Close, cons up a Move
     100                 : */
     101                 : 
     102                 : ////////////////////////////////////////////////////////////////////////////
     103                 : 
     104               0 : SkPath::SkPath() 
     105                 :     : fFillType(kWinding_FillType)
     106               0 :     , fBoundsIsDirty(true) {
     107               0 :     fConvexity = kUnknown_Convexity;
     108               0 :     fSegmentMask = 0;
     109                 : #ifdef SK_BUILD_FOR_ANDROID
     110                 :     fGenerationID = 0;
     111                 : #endif
     112               0 : }
     113                 : 
     114               0 : SkPath::SkPath(const SkPath& src) {
     115               0 :     SkDEBUGCODE(src.validate();)
     116               0 :     *this = src;
     117                 : #ifdef SK_BUILD_FOR_ANDROID
     118                 :     // the assignment operator above increments the ID so correct for that here
     119                 :     fGenerationID--;
     120                 : #endif
     121               0 : }
     122                 : 
     123               0 : SkPath::~SkPath() {
     124               0 :     SkDEBUGCODE(this->validate();)
     125               0 : }
     126                 : 
     127               0 : SkPath& SkPath::operator=(const SkPath& src) {
     128               0 :     SkDEBUGCODE(src.validate();)
     129                 : 
     130               0 :     if (this != &src) {
     131               0 :         fBounds         = src.fBounds;
     132               0 :         fPts            = src.fPts;
     133               0 :         fVerbs          = src.fVerbs;
     134               0 :         fFillType       = src.fFillType;
     135               0 :         fBoundsIsDirty  = src.fBoundsIsDirty;
     136               0 :         fConvexity      = src.fConvexity;
     137               0 :         fSegmentMask    = src.fSegmentMask;
     138                 :         GEN_ID_INC;
     139                 :     }
     140               0 :     SkDEBUGCODE(this->validate();)
     141               0 :     return *this;
     142                 : }
     143                 : 
     144               0 : bool operator==(const SkPath& a, const SkPath& b) {
     145                 :     // note: don't need to look at isConvex or bounds, since just comparing the
     146                 :     // raw data is sufficient.
     147                 : 
     148                 :     // We explicitly check fSegmentMask as a quick-reject. We could skip it,
     149                 :     // since it is only a cache of info in the fVerbs, but its a fast way to
     150                 :     // notice a difference
     151                 : 
     152                 :     return &a == &b ||
     153                 :         (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
     154               0 :          a.fVerbs == b.fVerbs && a.fPts == b.fPts);
     155                 : }
     156                 : 
     157               0 : void SkPath::swap(SkPath& other) {
     158               0 :     SkASSERT(&other != NULL);
     159                 : 
     160               0 :     if (this != &other) {
     161               0 :         SkTSwap<SkRect>(fBounds, other.fBounds);
     162               0 :         fPts.swap(other.fPts);
     163               0 :         fVerbs.swap(other.fVerbs);
     164               0 :         SkTSwap<uint8_t>(fFillType, other.fFillType);
     165               0 :         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
     166               0 :         SkTSwap<uint8_t>(fConvexity, other.fConvexity);
     167               0 :         SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
     168                 :         GEN_ID_INC;
     169                 :     }
     170               0 : }
     171                 : 
     172                 : #ifdef SK_BUILD_FOR_ANDROID
     173                 : uint32_t SkPath::getGenerationID() const {
     174                 :     return fGenerationID;
     175                 : }
     176                 : #endif
     177                 : 
     178               0 : void SkPath::reset() {
     179               0 :     SkDEBUGCODE(this->validate();)
     180                 : 
     181               0 :     fPts.reset();
     182               0 :     fVerbs.reset();
     183                 :     GEN_ID_INC;
     184               0 :     fBoundsIsDirty = true;
     185               0 :     fConvexity = kUnknown_Convexity;
     186               0 :     fSegmentMask = 0;
     187               0 : }
     188                 : 
     189               0 : void SkPath::rewind() {
     190               0 :     SkDEBUGCODE(this->validate();)
     191                 : 
     192               0 :     fPts.rewind();
     193               0 :     fVerbs.rewind();
     194                 :     GEN_ID_INC;
     195               0 :     fConvexity = kUnknown_Convexity;
     196               0 :     fBoundsIsDirty = true;
     197               0 :     fSegmentMask = 0;
     198               0 : }
     199                 : 
     200               0 : bool SkPath::isEmpty() const {
     201               0 :     SkDEBUGCODE(this->validate();)
     202                 : #if SK_OLD_EMPTY_PATH_BEHAVIOR
     203                 :     int count = fVerbs.count();
     204                 :     return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
     205                 : #else
     206               0 :     return 0 == fVerbs.count();
     207                 : #endif
     208                 : }
     209                 : 
     210                 : /*
     211                 :  Determines if path is a rect by keeping track of changes in direction
     212                 :  and looking for a loop either clockwise or counterclockwise.
     213                 :  
     214                 :  The direction is computed such that:
     215                 :   0: vertical up
     216                 :   1: horizontal right
     217                 :   2: vertical down
     218                 :   3: horizontal left
     219                 :  
     220                 : A rectangle cycles up/right/down/left or up/left/down/right.
     221                 : 
     222                 : The test fails if:
     223                 :   The path is closed, and followed by a line.
     224                 :   A second move creates a new endpoint.
     225                 :   A diagonal line is parsed.
     226                 :   There's more than four changes of direction.
     227                 :   There's a discontinuity on the line (e.g., a move in the middle)
     228                 :   The line reverses direction.
     229                 :   The rectangle doesn't complete a cycle.
     230                 :   The path contains a quadratic or cubic.
     231                 :   The path contains fewer than four points.
     232                 :   The final point isn't equal to the first point.
     233                 :   
     234                 : It's OK if the path has:
     235                 :   Several colinear line segments composing a rectangle side.
     236                 :   Single points on the rectangle side.
     237                 :   
     238                 : The direction takes advantage of the corners found since opposite sides
     239                 : must travel in opposite directions.
     240                 : 
     241                 : FIXME: Allow colinear quads and cubics to be treated like lines.
     242                 : FIXME: If the API passes fill-only, return true if the filled stroke
     243                 :        is a rectangle, though the caller failed to close the path.
     244                 :  */
     245               0 : bool SkPath::isRect(SkRect* rect) const {
     246               0 :     SkDEBUGCODE(this->validate();)
     247                 : 
     248               0 :     int corners = 0;
     249                 :     SkPoint first, last;
     250               0 :     first.set(0, 0);
     251               0 :     last.set(0, 0);
     252               0 :     int firstDirection = 0;
     253               0 :     int lastDirection = 0;
     254               0 :     int nextDirection = 0;
     255               0 :     bool closedOrMoved = false;
     256               0 :     bool autoClose = false;
     257               0 :     const uint8_t* verbs = fVerbs.begin();
     258               0 :     const uint8_t* verbStop = fVerbs.end();
     259               0 :     const SkPoint* pts = fPts.begin();
     260               0 :     while (verbs != verbStop) {
     261               0 :         switch (*verbs++) {
     262                 :             case kClose_Verb:
     263               0 :                 pts = fPts.begin();
     264               0 :                 autoClose = true;
     265                 :             case kLine_Verb: {
     266               0 :                 SkScalar left = last.fX;
     267               0 :                 SkScalar top = last.fY;
     268               0 :                 SkScalar right = pts->fX;
     269               0 :                 SkScalar bottom = pts->fY;
     270               0 :                 ++pts;
     271               0 :                 if (left != right && top != bottom) {
     272               0 :                     return false; // diagonal
     273                 :                 }
     274               0 :                 if (left == right && top == bottom) {
     275               0 :                     break; // single point on side OK
     276                 :                 }
     277                 :                 nextDirection = (left != right) << 0 |
     278               0 :                     (left < right || top < bottom) << 1;
     279               0 :                 if (0 == corners) {
     280               0 :                     firstDirection = nextDirection;
     281               0 :                     first = last;
     282               0 :                     last = pts[-1];
     283               0 :                     corners = 1;
     284               0 :                     closedOrMoved = false;
     285               0 :                     break;
     286                 :                 }
     287               0 :                 if (closedOrMoved) {
     288               0 :                     return false; // closed followed by a line
     289                 :                 }
     290               0 :                 closedOrMoved = autoClose;
     291               0 :                 if (lastDirection != nextDirection) {
     292               0 :                     if (++corners > 4) {
     293               0 :                         return false; // too many direction changes
     294                 :                     }
     295                 :                 }
     296               0 :                 last = pts[-1];
     297               0 :                 if (lastDirection == nextDirection) {
     298               0 :                     break; // colinear segment
     299                 :                 }
     300                 :                 // Possible values for corners are 2, 3, and 4.
     301                 :                 // When corners == 3, nextDirection opposes firstDirection.
     302                 :                 // Otherwise, nextDirection at corner 2 opposes corner 4.
     303               0 :                 int turn = firstDirection ^ (corners - 1);
     304               0 :                 int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
     305               0 :                 if ((directionCycle ^ turn) != nextDirection) {
     306               0 :                     return false; // direction didn't follow cycle
     307                 :                 }
     308               0 :                 break;
     309                 :             }
     310                 :             case kQuad_Verb:
     311                 :             case kCubic_Verb:
     312               0 :                 return false; // quadratic, cubic not allowed
     313                 :             case kMove_Verb:
     314               0 :                 last = *pts++;
     315               0 :                 closedOrMoved = true;
     316               0 :                 break;
     317                 :         }
     318               0 :         lastDirection = nextDirection;
     319                 :     }
     320                 :     // Success if 4 corners and first point equals last
     321               0 :     bool result = 4 == corners && first == last;
     322               0 :     if (result && rect) {
     323               0 :         *rect = getBounds();
     324                 :     }
     325               0 :     return result;
     326                 : }
     327                 : 
     328               0 : int SkPath::getPoints(SkPoint copy[], int max) const {
     329               0 :     SkDEBUGCODE(this->validate();)
     330                 : 
     331               0 :     SkASSERT(max >= 0);
     332               0 :     int count = fPts.count();
     333               0 :     if (copy && max > 0 && count > 0) {
     334               0 :         memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
     335                 :     }
     336               0 :     return count;
     337                 : }
     338                 : 
     339               0 : SkPoint SkPath::getPoint(int index) const {
     340               0 :     if ((unsigned)index < (unsigned)fPts.count()) {
     341               0 :         return fPts[index];
     342                 :     }
     343               0 :     return SkPoint::Make(0, 0);
     344                 : }
     345                 : 
     346               0 : bool SkPath::getLastPt(SkPoint* lastPt) const {
     347               0 :     SkDEBUGCODE(this->validate();)
     348                 : 
     349               0 :     int count = fPts.count();
     350               0 :     if (count > 0) {
     351               0 :         if (lastPt) {
     352               0 :             *lastPt = fPts[count - 1];
     353                 :         }
     354               0 :         return true;
     355                 :     }
     356               0 :     if (lastPt) {
     357               0 :         lastPt->set(0, 0);
     358                 :     }
     359               0 :     return false;
     360                 : }
     361                 : 
     362               0 : void SkPath::setLastPt(SkScalar x, SkScalar y) {
     363               0 :     SkDEBUGCODE(this->validate();)
     364                 : 
     365               0 :     int count = fPts.count();
     366               0 :     if (count == 0) {
     367               0 :         this->moveTo(x, y);
     368                 :     } else {
     369               0 :         fPts[count - 1].set(x, y);
     370                 :         GEN_ID_INC;
     371                 :     }
     372               0 : }
     373                 : 
     374               0 : void SkPath::computeBounds() const {
     375               0 :     SkDEBUGCODE(this->validate();)
     376               0 :     SkASSERT(fBoundsIsDirty);
     377                 : 
     378               0 :     fBoundsIsDirty = false;
     379               0 :     compute_pt_bounds(&fBounds, fPts);
     380               0 : }
     381                 : 
     382               0 : void SkPath::setConvexity(Convexity c) {
     383               0 :     if (fConvexity != c) {
     384               0 :         fConvexity = c;
     385                 :         GEN_ID_INC;
     386                 :     }
     387               0 : }
     388                 : 
     389                 : //////////////////////////////////////////////////////////////////////////////
     390                 : //  Construction methods
     391                 : 
     392                 : #define DIRTY_AFTER_EDIT                 \
     393                 :     do {                                 \
     394                 :         fBoundsIsDirty = true;           \
     395                 :         fConvexity = kUnknown_Convexity; \
     396                 :     } while (0)
     397                 : 
     398               0 : void SkPath::incReserve(U16CPU inc) {
     399               0 :     SkDEBUGCODE(this->validate();)
     400                 : 
     401               0 :     fVerbs.setReserve(fVerbs.count() + inc);
     402               0 :     fPts.setReserve(fPts.count() + inc);
     403                 : 
     404               0 :     SkDEBUGCODE(this->validate();)
     405               0 : }
     406                 : 
     407               0 : void SkPath::moveTo(SkScalar x, SkScalar y) {
     408               0 :     SkDEBUGCODE(this->validate();)
     409                 : 
     410               0 :     int      vc = fVerbs.count();
     411                 :     SkPoint* pt;
     412                 : 
     413                 : #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
     414                 :     if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
     415                 :         pt = &fPts[fPts.count() - 1];
     416                 :     } else {
     417                 :         pt = fPts.append();
     418                 :         *fVerbs.append() = kMove_Verb;
     419                 :     }
     420                 : #else
     421               0 :     pt = fPts.append();
     422               0 :     *fVerbs.append() = kMove_Verb;
     423                 : #endif
     424               0 :     pt->set(x, y);
     425                 : 
     426                 :     GEN_ID_INC;
     427               0 :     DIRTY_AFTER_EDIT;
     428               0 : }
     429                 : 
     430               0 : void SkPath::rMoveTo(SkScalar x, SkScalar y) {
     431                 :     SkPoint pt;
     432               0 :     this->getLastPt(&pt);
     433               0 :     this->moveTo(pt.fX + x, pt.fY + y);
     434               0 : }
     435                 : 
     436               0 : void SkPath::lineTo(SkScalar x, SkScalar y) {
     437               0 :     SkDEBUGCODE(this->validate();)
     438                 : 
     439               0 :     if (fVerbs.count() == 0) {
     440               0 :         fPts.append()->set(0, 0);
     441               0 :         *fVerbs.append() = kMove_Verb;
     442                 :     }
     443               0 :     fPts.append()->set(x, y);
     444               0 :     *fVerbs.append() = kLine_Verb;
     445               0 :     fSegmentMask |= kLine_SegmentMask;
     446                 : 
     447                 :     GEN_ID_INC;
     448               0 :     DIRTY_AFTER_EDIT;
     449               0 : }
     450                 : 
     451               0 : void SkPath::rLineTo(SkScalar x, SkScalar y) {
     452                 :     SkPoint pt;
     453               0 :     this->getLastPt(&pt);
     454               0 :     this->lineTo(pt.fX + x, pt.fY + y);
     455               0 : }
     456                 : 
     457               0 : void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     458               0 :     SkDEBUGCODE(this->validate();)
     459                 : 
     460               0 :     if (fVerbs.count() == 0) {
     461               0 :         fPts.append()->set(0, 0);
     462               0 :         *fVerbs.append() = kMove_Verb;
     463                 :     }
     464                 : 
     465               0 :     SkPoint* pts = fPts.append(2);
     466               0 :     pts[0].set(x1, y1);
     467               0 :     pts[1].set(x2, y2);
     468               0 :     *fVerbs.append() = kQuad_Verb;
     469               0 :     fSegmentMask |= kQuad_SegmentMask;
     470                 : 
     471                 :     GEN_ID_INC;
     472               0 :     DIRTY_AFTER_EDIT;
     473               0 : }
     474                 : 
     475               0 : void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     476                 :     SkPoint pt;
     477               0 :     this->getLastPt(&pt);
     478               0 :     this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
     479               0 : }
     480                 : 
     481               0 : void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     482                 :                      SkScalar x3, SkScalar y3) {
     483               0 :     SkDEBUGCODE(this->validate();)
     484                 : 
     485               0 :     if (fVerbs.count() == 0) {
     486               0 :         fPts.append()->set(0, 0);
     487               0 :         *fVerbs.append() = kMove_Verb;
     488                 :     }
     489               0 :     SkPoint* pts = fPts.append(3);
     490               0 :     pts[0].set(x1, y1);
     491               0 :     pts[1].set(x2, y2);
     492               0 :     pts[2].set(x3, y3);
     493               0 :     *fVerbs.append() = kCubic_Verb;
     494               0 :     fSegmentMask |= kCubic_SegmentMask;
     495                 : 
     496                 :     GEN_ID_INC;
     497               0 :     DIRTY_AFTER_EDIT;
     498               0 : }
     499                 : 
     500               0 : void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     501                 :                       SkScalar x3, SkScalar y3) {
     502                 :     SkPoint pt;
     503               0 :     this->getLastPt(&pt);
     504                 :     this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
     505               0 :                   pt.fX + x3, pt.fY + y3);
     506               0 : }
     507                 : 
     508               0 : void SkPath::close() {
     509               0 :     SkDEBUGCODE(this->validate();)
     510                 : 
     511               0 :     int count = fVerbs.count();
     512               0 :     if (count > 0) {
     513               0 :         switch (fVerbs[count - 1]) {
     514                 :             case kLine_Verb:
     515                 :             case kQuad_Verb:
     516                 :             case kCubic_Verb:
     517                 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
     518                 :             case kMove_Verb:
     519                 : #endif
     520               0 :                 *fVerbs.append() = kClose_Verb;
     521                 :                 GEN_ID_INC;
     522               0 :                 break;
     523                 :             default:
     524                 :                 // don't add a close if it's the first verb or a repeat
     525               0 :                 break;
     526                 :         }
     527                 :     }
     528               0 : }
     529                 : 
     530                 : ///////////////////////////////////////////////////////////////////////////////
     531                 : 
     532               0 : void SkPath::addRect(const SkRect& rect, Direction dir) {
     533               0 :     this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
     534               0 : }
     535                 : 
     536               0 : void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
     537                 :                      SkScalar bottom, Direction dir) {
     538               0 :     SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
     539                 : 
     540               0 :     this->incReserve(5);
     541                 : 
     542               0 :     this->moveTo(left, top);
     543               0 :     if (dir == kCCW_Direction) {
     544               0 :         this->lineTo(left, bottom);
     545               0 :         this->lineTo(right, bottom);
     546               0 :         this->lineTo(right, top);
     547                 :     } else {
     548               0 :         this->lineTo(right, top);
     549               0 :         this->lineTo(right, bottom);
     550               0 :         this->lineTo(left, bottom);
     551                 :     }
     552               0 :     this->close();
     553               0 : }
     554                 : 
     555                 : #define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
     556                 : 
     557               0 : void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
     558                 :                           Direction dir) {
     559               0 :     SkScalar    w = rect.width();
     560               0 :     SkScalar    halfW = SkScalarHalf(w);
     561               0 :     SkScalar    h = rect.height();
     562               0 :     SkScalar    halfH = SkScalarHalf(h);
     563                 : 
     564               0 :     if (halfW <= 0 || halfH <= 0) {
     565               0 :         return;
     566                 :     }
     567                 : 
     568               0 :     bool skip_hori = rx >= halfW;
     569               0 :     bool skip_vert = ry >= halfH;
     570                 : 
     571               0 :     if (skip_hori && skip_vert) {
     572               0 :         this->addOval(rect, dir);
     573               0 :         return;
     574                 :     }
     575                 : 
     576               0 :     SkAutoPathBoundsUpdate apbu(this, rect);
     577                 : 
     578               0 :     if (skip_hori) {
     579               0 :         rx = halfW;
     580               0 :     } else if (skip_vert) {
     581               0 :         ry = halfH;
     582                 :     }
     583                 : 
     584               0 :     SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
     585               0 :     SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
     586                 : 
     587               0 :     this->incReserve(17);
     588               0 :     this->moveTo(rect.fRight - rx, rect.fTop);
     589               0 :     if (dir == kCCW_Direction) {
     590               0 :         if (!skip_hori) {
     591               0 :             this->lineTo(rect.fLeft + rx, rect.fTop);       // top
     592                 :         }
     593                 :         this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
     594                 :                       rect.fLeft, rect.fTop + ry - sy,
     595               0 :                       rect.fLeft, rect.fTop + ry);          // top-left
     596               0 :         if (!skip_vert) {
     597               0 :             this->lineTo(rect.fLeft, rect.fBottom - ry);        // left
     598                 :         }
     599                 :         this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
     600                 :                       rect.fLeft + rx - sx, rect.fBottom,
     601               0 :                       rect.fLeft + rx, rect.fBottom);       // bot-left
     602               0 :         if (!skip_hori) {
     603               0 :             this->lineTo(rect.fRight - rx, rect.fBottom);   // bottom
     604                 :         }
     605                 :         this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
     606                 :                       rect.fRight, rect.fBottom - ry + sy,
     607               0 :                       rect.fRight, rect.fBottom - ry);      // bot-right
     608               0 :         if (!skip_vert) {
     609               0 :             this->lineTo(rect.fRight, rect.fTop + ry);
     610                 :         }
     611                 :         this->cubicTo(rect.fRight, rect.fTop + ry - sy,
     612                 :                       rect.fRight - rx + sx, rect.fTop,
     613               0 :                       rect.fRight - rx, rect.fTop);         // top-right
     614                 :     } else {
     615                 :         this->cubicTo(rect.fRight - rx + sx, rect.fTop,
     616                 :                       rect.fRight, rect.fTop + ry - sy,
     617               0 :                       rect.fRight, rect.fTop + ry);         // top-right
     618               0 :         if (!skip_vert) {
     619               0 :             this->lineTo(rect.fRight, rect.fBottom - ry);
     620                 :         }
     621                 :         this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
     622                 :                       rect.fRight - rx + sx, rect.fBottom,
     623               0 :                       rect.fRight - rx, rect.fBottom);      // bot-right
     624               0 :         if (!skip_hori) {
     625               0 :             this->lineTo(rect.fLeft + rx, rect.fBottom);    // bottom
     626                 :         }
     627                 :         this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
     628                 :                       rect.fLeft, rect.fBottom - ry + sy,
     629               0 :                       rect.fLeft, rect.fBottom - ry);       // bot-left
     630               0 :         if (!skip_vert) {
     631               0 :             this->lineTo(rect.fLeft, rect.fTop + ry);       // left
     632                 :         }
     633                 :         this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
     634                 :                       rect.fLeft + rx - sx, rect.fTop,
     635               0 :                       rect.fLeft + rx, rect.fTop);          // top-left
     636               0 :         if (!skip_hori) {
     637               0 :             this->lineTo(rect.fRight - rx, rect.fTop);      // top
     638                 :         }
     639                 :     }
     640               0 :     this->close();
     641                 : }
     642                 : 
     643               0 : static void add_corner_arc(SkPath* path, const SkRect& rect,
     644                 :                            SkScalar rx, SkScalar ry, int startAngle,
     645                 :                            SkPath::Direction dir, bool forceMoveTo) {
     646               0 :     rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
     647               0 :     ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
     648                 : 
     649                 :     SkRect   r;
     650               0 :     r.set(-rx, -ry, rx, ry);
     651                 : 
     652               0 :     switch (startAngle) {
     653                 :         case   0:
     654               0 :             r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
     655               0 :             break;
     656                 :         case  90:
     657               0 :             r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
     658               0 :             break;
     659               0 :         case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
     660               0 :         case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
     661               0 :         default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
     662                 :     }
     663                 : 
     664               0 :     SkScalar start = SkIntToScalar(startAngle);
     665               0 :     SkScalar sweep = SkIntToScalar(90);
     666               0 :     if (SkPath::kCCW_Direction == dir) {
     667               0 :         start += sweep;
     668               0 :         sweep = -sweep;
     669                 :     }
     670                 : 
     671               0 :     path->arcTo(r, start, sweep, forceMoveTo);
     672               0 : }
     673                 : 
     674               0 : void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
     675                 :                           Direction dir) {
     676                 :     // abort before we invoke SkAutoPathBoundsUpdate()
     677               0 :     if (rect.isEmpty()) {
     678               0 :         return;
     679                 :     }
     680                 : 
     681               0 :     SkAutoPathBoundsUpdate apbu(this, rect);
     682                 : 
     683               0 :     if (kCW_Direction == dir) {
     684               0 :         add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
     685               0 :         add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
     686               0 :         add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
     687               0 :         add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
     688                 :     } else {
     689               0 :         add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
     690               0 :         add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
     691               0 :         add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
     692               0 :         add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
     693                 :     }
     694               0 :     this->close();
     695                 : }
     696                 : 
     697               0 : void SkPath::addOval(const SkRect& oval, Direction dir) {
     698               0 :     SkAutoPathBoundsUpdate apbu(this, oval);
     699                 : 
     700               0 :     SkScalar    cx = oval.centerX();
     701               0 :     SkScalar    cy = oval.centerY();
     702               0 :     SkScalar    rx = SkScalarHalf(oval.width());
     703               0 :     SkScalar    ry = SkScalarHalf(oval.height());
     704                 : #if 0   // these seem faster than using quads (1/2 the number of edges)
     705                 :     SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
     706                 :     SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
     707                 : 
     708                 :     this->incReserve(13);
     709                 :     this->moveTo(cx + rx, cy);
     710                 :     if (dir == kCCW_Direction) {
     711                 :         this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
     712                 :         this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
     713                 :         this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
     714                 :         this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
     715                 :     } else {
     716                 :         this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
     717                 :         this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
     718                 :         this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
     719                 :         this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
     720                 :     }
     721                 : #else
     722               0 :     SkScalar    sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
     723               0 :     SkScalar    sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
     724               0 :     SkScalar    mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
     725               0 :     SkScalar    my = SkScalarMul(ry, SK_ScalarRoot2Over2);
     726                 : 
     727                 :     /*
     728                 :         To handle imprecision in computing the center and radii, we revert to
     729                 :         the provided bounds when we can (i.e. use oval.fLeft instead of cx-rx)
     730                 :         to ensure that we don't exceed the oval's bounds *ever*, since we want
     731                 :         to use oval for our fast-bounds, rather than have to recompute it.
     732                 :     */
     733               0 :     const SkScalar L = oval.fLeft;      // cx - rx
     734               0 :     const SkScalar T = oval.fTop;       // cy - ry
     735               0 :     const SkScalar R = oval.fRight;     // cx + rx
     736               0 :     const SkScalar B = oval.fBottom;    // cy + ry
     737                 : 
     738               0 :     this->incReserve(17);   // 8 quads + close
     739               0 :     this->moveTo(R, cy);
     740               0 :     if (dir == kCCW_Direction) {
     741               0 :         this->quadTo(      R, cy - sy, cx + mx, cy - my);
     742               0 :         this->quadTo(cx + sx,       T, cx     ,       T);
     743               0 :         this->quadTo(cx - sx,       T, cx - mx, cy - my);
     744               0 :         this->quadTo(      L, cy - sy,       L, cy     );
     745               0 :         this->quadTo(      L, cy + sy, cx - mx, cy + my);
     746               0 :         this->quadTo(cx - sx,       B, cx     ,       B);
     747               0 :         this->quadTo(cx + sx,       B, cx + mx, cy + my);
     748               0 :         this->quadTo(      R, cy + sy,       R, cy     );
     749                 :     } else {
     750               0 :         this->quadTo(      R, cy + sy, cx + mx, cy + my);
     751               0 :         this->quadTo(cx + sx,       B, cx     ,       B);
     752               0 :         this->quadTo(cx - sx,       B, cx - mx, cy + my);
     753               0 :         this->quadTo(      L, cy + sy,       L, cy     );
     754               0 :         this->quadTo(      L, cy - sy, cx - mx, cy - my);
     755               0 :         this->quadTo(cx - sx,       T, cx     ,       T);
     756               0 :         this->quadTo(cx + sx,       T, cx + mx, cy - my);
     757               0 :         this->quadTo(      R, cy - sy,       R, cy     );
     758                 :     }
     759                 : #endif
     760               0 :     this->close();
     761               0 : }
     762                 : 
     763               0 : void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
     764               0 :     if (r > 0) {
     765                 :         SkRect  rect;
     766               0 :         rect.set(x - r, y - r, x + r, y + r);
     767               0 :         this->addOval(rect, dir);
     768                 :     }
     769               0 : }
     770                 : 
     771                 : #include "SkGeometry.h"
     772                 : 
     773               0 : static int build_arc_points(const SkRect& oval, SkScalar startAngle,
     774                 :                             SkScalar sweepAngle,
     775                 :                             SkPoint pts[kSkBuildQuadArcStorage]) {
     776                 :     SkVector start, stop;
     777                 : 
     778               0 :     start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
     779                 :     stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
     780               0 :                              &stop.fX);
     781                 : 
     782                 :     /*  If the sweep angle is nearly (but less than) 360, then due to precision
     783                 :         loss in radians-conversion and/or sin/cos, we may end up with coincident
     784                 :         vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
     785                 :         of drawing a nearly complete circle (good).
     786                 :              e.g. canvas.drawArc(0, 359.99, ...)
     787                 :              -vs- canvas.drawArc(0, 359.9, ...)
     788                 :         We try to detect this edge case, and tweak the stop vector
     789                 :      */
     790               0 :     if (start == stop) {
     791               0 :         SkScalar sw = SkScalarAbs(sweepAngle);
     792               0 :         if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
     793               0 :             SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
     794                 :             // make a guess at a tiny angle (in radians) to tweak by
     795               0 :             SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
     796                 :             // not sure how much will be enough, so we use a loop
     797               0 :             do {
     798               0 :                 stopRad -= deltaRad;
     799               0 :                 stop.fY = SkScalarSinCos(stopRad, &stop.fX);
     800                 :             } while (start == stop);
     801                 :         }
     802                 :     }
     803                 : 
     804                 :     SkMatrix    matrix;
     805                 : 
     806               0 :     matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
     807               0 :     matrix.postTranslate(oval.centerX(), oval.centerY());
     808                 : 
     809                 :     return SkBuildQuadArc(start, stop,
     810                 :           sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
     811               0 :                           &matrix, pts);
     812                 : }
     813                 : 
     814               0 : void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
     815                 :                    bool forceMoveTo) {
     816               0 :     if (oval.width() < 0 || oval.height() < 0) {
     817               0 :         return;
     818                 :     }
     819                 : 
     820                 :     SkPoint pts[kSkBuildQuadArcStorage];
     821               0 :     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
     822               0 :     SkASSERT((count & 1) == 1);
     823                 : 
     824               0 :     if (fVerbs.count() == 0) {
     825               0 :         forceMoveTo = true;
     826                 :     }
     827               0 :     this->incReserve(count);
     828               0 :     forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
     829               0 :     for (int i = 1; i < count; i += 2) {
     830               0 :         this->quadTo(pts[i], pts[i+1]);
     831                 :     }
     832                 : }
     833                 : 
     834               0 : void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
     835                 :                     SkScalar sweepAngle) {
     836               0 :     if (oval.isEmpty() || 0 == sweepAngle) {
     837               0 :         return;
     838                 :     }
     839                 : 
     840               0 :     const SkScalar kFullCircleAngle = SkIntToScalar(360);
     841                 : 
     842               0 :     if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
     843               0 :         this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
     844               0 :         return;
     845                 :     }
     846                 : 
     847                 :     SkPoint pts[kSkBuildQuadArcStorage];
     848               0 :     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
     849                 : 
     850               0 :     this->incReserve(count);
     851               0 :     this->moveTo(pts[0]);
     852               0 :     for (int i = 1; i < count; i += 2) {
     853               0 :         this->quadTo(pts[i], pts[i+1]);
     854                 :     }
     855                 : }
     856                 : 
     857                 : /*
     858                 :     Need to handle the case when the angle is sharp, and our computed end-points
     859                 :     for the arc go behind pt1 and/or p2...
     860                 : */
     861               0 : void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     862                 :                    SkScalar radius) {
     863                 :     SkVector    before, after;
     864                 : 
     865                 :     // need to know our prev pt so we can construct tangent vectors
     866                 :     {
     867                 :         SkPoint start;
     868               0 :         this->getLastPt(&start);
     869                 :         // Handle degenerate cases by adding a line to the first point and
     870                 :         // bailing out.
     871               0 :         if ((x1 == start.fX && y1 == start.fY) ||
     872                 :             (x1 == x2 && y1 == y2) ||
     873                 :             radius == 0) {
     874               0 :             this->lineTo(x1, y1);
     875               0 :             return;
     876                 :         }
     877               0 :         before.setNormalize(x1 - start.fX, y1 - start.fY);
     878               0 :         after.setNormalize(x2 - x1, y2 - y1);
     879                 :     }
     880                 : 
     881               0 :     SkScalar cosh = SkPoint::DotProduct(before, after);
     882               0 :     SkScalar sinh = SkPoint::CrossProduct(before, after);
     883                 : 
     884               0 :     if (SkScalarNearlyZero(sinh)) {   // angle is too tight
     885               0 :         this->lineTo(x1, y1);
     886               0 :         return;
     887                 :     }
     888                 : 
     889               0 :     SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
     890               0 :     if (dist < 0) {
     891               0 :         dist = -dist;
     892                 :     }
     893                 : 
     894               0 :     SkScalar xx = x1 - SkScalarMul(dist, before.fX);
     895               0 :     SkScalar yy = y1 - SkScalarMul(dist, before.fY);
     896                 :     SkRotationDirection arcDir;
     897                 : 
     898                 :     // now turn before/after into normals
     899               0 :     if (sinh > 0) {
     900               0 :         before.rotateCCW();
     901               0 :         after.rotateCCW();
     902               0 :         arcDir = kCW_SkRotationDirection;
     903                 :     } else {
     904               0 :         before.rotateCW();
     905               0 :         after.rotateCW();
     906               0 :         arcDir = kCCW_SkRotationDirection;
     907                 :     }
     908                 : 
     909                 :     SkMatrix    matrix;
     910                 :     SkPoint     pts[kSkBuildQuadArcStorage];
     911                 : 
     912               0 :     matrix.setScale(radius, radius);
     913                 :     matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
     914               0 :                          yy - SkScalarMul(radius, before.fY));
     915                 : 
     916               0 :     int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
     917                 : 
     918               0 :     this->incReserve(count);
     919                 :     // [xx,yy] == pts[0]
     920               0 :     this->lineTo(xx, yy);
     921               0 :     for (int i = 1; i < count; i += 2) {
     922               0 :         this->quadTo(pts[i], pts[i+1]);
     923                 :     }
     924                 : }
     925                 : 
     926                 : ///////////////////////////////////////////////////////////////////////////////
     927                 : 
     928               0 : void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
     929                 :     SkMatrix matrix;
     930                 : 
     931               0 :     matrix.setTranslate(dx, dy);
     932               0 :     this->addPath(path, matrix);
     933               0 : }
     934                 : 
     935               0 : void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
     936               0 :     this->incReserve(path.fPts.count());
     937                 : 
     938               0 :     RawIter iter(path);
     939                 :     SkPoint pts[4];
     940                 :     Verb    verb;
     941                 : 
     942               0 :     SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
     943                 : 
     944               0 :     while ((verb = iter.next(pts)) != kDone_Verb) {
     945               0 :         switch (verb) {
     946                 :             case kMove_Verb:
     947               0 :                 proc(matrix, &pts[0], &pts[0], 1);
     948               0 :                 this->moveTo(pts[0]);
     949               0 :                 break;
     950                 :             case kLine_Verb:
     951               0 :                 proc(matrix, &pts[1], &pts[1], 1);
     952               0 :                 this->lineTo(pts[1]);
     953               0 :                 break;
     954                 :             case kQuad_Verb:
     955               0 :                 proc(matrix, &pts[1], &pts[1], 2);
     956               0 :                 this->quadTo(pts[1], pts[2]);
     957               0 :                 break;
     958                 :             case kCubic_Verb:
     959               0 :                 proc(matrix, &pts[1], &pts[1], 3);
     960               0 :                 this->cubicTo(pts[1], pts[2], pts[3]);
     961               0 :                 break;
     962                 :             case kClose_Verb:
     963               0 :                 this->close();
     964               0 :                 break;
     965                 :             default:
     966               0 :                 SkDEBUGFAIL("unknown verb");
     967                 :         }
     968                 :     }
     969               0 : }
     970                 : 
     971                 : ///////////////////////////////////////////////////////////////////////////////
     972                 : 
     973                 : static const uint8_t gPtsInVerb[] = {
     974                 :     1,  // kMove
     975                 :     1,  // kLine
     976                 :     2,  // kQuad
     977                 :     3,  // kCubic
     978                 :     0,  // kClose
     979                 :     0   // kDone
     980                 : };
     981                 : 
     982                 : // ignore the initial moveto, and stop when the 1st contour ends
     983               0 : void SkPath::pathTo(const SkPath& path) {
     984               0 :     int i, vcount = path.fVerbs.count();
     985               0 :     if (vcount == 0) {
     986               0 :         return;
     987                 :     }
     988                 : 
     989               0 :     this->incReserve(vcount);
     990                 : 
     991               0 :     const uint8_t*  verbs = path.fVerbs.begin();
     992               0 :     const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
     993                 : 
     994               0 :     SkASSERT(verbs[0] == kMove_Verb);
     995               0 :     for (i = 1; i < vcount; i++) {
     996               0 :         switch (verbs[i]) {
     997                 :             case kLine_Verb:
     998               0 :                 this->lineTo(pts[0].fX, pts[0].fY);
     999               0 :                 break;
    1000                 :             case kQuad_Verb:
    1001               0 :                 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
    1002               0 :                 break;
    1003                 :             case kCubic_Verb:
    1004               0 :                 this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
    1005               0 :                               pts[2].fX, pts[2].fY);
    1006               0 :                 break;
    1007                 :             case kClose_Verb:
    1008               0 :                 return;
    1009                 :         }
    1010               0 :         pts += gPtsInVerb[verbs[i]];
    1011                 :     }
    1012                 : }
    1013                 : 
    1014                 : // ignore the last point of the 1st contour
    1015               0 : void SkPath::reversePathTo(const SkPath& path) {
    1016               0 :     int i, vcount = path.fVerbs.count();
    1017               0 :     if (vcount == 0) {
    1018               0 :         return;
    1019                 :     }
    1020                 : 
    1021               0 :     this->incReserve(vcount);
    1022                 : 
    1023               0 :     const uint8_t*  verbs = path.fVerbs.begin();
    1024               0 :     const SkPoint*  pts = path.fPts.begin();
    1025                 : 
    1026               0 :     SkASSERT(verbs[0] == kMove_Verb);
    1027               0 :     for (i = 1; i < vcount; i++) {
    1028               0 :         int n = gPtsInVerb[verbs[i]];
    1029               0 :         if (n == 0) {
    1030               0 :             break;
    1031                 :         }
    1032               0 :         pts += n;
    1033                 :     }
    1034                 : 
    1035               0 :     while (--i > 0) {
    1036               0 :         switch (verbs[i]) {
    1037                 :             case kLine_Verb:
    1038               0 :                 this->lineTo(pts[-1].fX, pts[-1].fY);
    1039               0 :                 break;
    1040                 :             case kQuad_Verb:
    1041               0 :                 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
    1042               0 :                 break;
    1043                 :             case kCubic_Verb:
    1044               0 :                 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
    1045               0 :                               pts[-3].fX, pts[-3].fY);
    1046               0 :                 break;
    1047                 :             default:
    1048               0 :                 SkDEBUGFAIL("bad verb");
    1049               0 :                 break;
    1050                 :         }
    1051               0 :         pts -= gPtsInVerb[verbs[i]];
    1052                 :     }
    1053                 : }
    1054                 : 
    1055                 : ///////////////////////////////////////////////////////////////////////////////
    1056                 : 
    1057               0 : void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
    1058                 :     SkMatrix    matrix;
    1059                 : 
    1060               0 :     matrix.setTranslate(dx, dy);
    1061               0 :     this->transform(matrix, dst);
    1062               0 : }
    1063                 : 
    1064                 : #include "SkGeometry.h"
    1065                 : 
    1066               0 : static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
    1067                 :                               int level = 2) {
    1068               0 :     if (--level >= 0) {
    1069                 :         SkPoint tmp[5];
    1070                 : 
    1071               0 :         SkChopQuadAtHalf(pts, tmp);
    1072               0 :         subdivide_quad_to(path, &tmp[0], level);
    1073               0 :         subdivide_quad_to(path, &tmp[2], level);
    1074                 :     } else {
    1075               0 :         path->quadTo(pts[1], pts[2]);
    1076                 :     }
    1077               0 : }
    1078                 : 
    1079               0 : static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
    1080                 :                                int level = 2) {
    1081               0 :     if (--level >= 0) {
    1082                 :         SkPoint tmp[7];
    1083                 : 
    1084               0 :         SkChopCubicAtHalf(pts, tmp);
    1085               0 :         subdivide_cubic_to(path, &tmp[0], level);
    1086               0 :         subdivide_cubic_to(path, &tmp[3], level);
    1087                 :     } else {
    1088               0 :         path->cubicTo(pts[1], pts[2], pts[3]);
    1089                 :     }
    1090               0 : }
    1091                 : 
    1092               0 : void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
    1093               0 :     SkDEBUGCODE(this->validate();)
    1094               0 :     if (dst == NULL) {
    1095               0 :         dst = (SkPath*)this;
    1096                 :     }
    1097                 : 
    1098               0 :     if (matrix.hasPerspective()) {
    1099               0 :         SkPath  tmp;
    1100               0 :         tmp.fFillType = fFillType;
    1101                 : 
    1102               0 :         SkPath::Iter    iter(*this, false);
    1103                 :         SkPoint         pts[4];
    1104                 :         SkPath::Verb    verb;
    1105                 : 
    1106               0 :         while ((verb = iter.next(pts)) != kDone_Verb) {
    1107               0 :             switch (verb) {
    1108                 :                 case kMove_Verb:
    1109               0 :                     tmp.moveTo(pts[0]);
    1110               0 :                     break;
    1111                 :                 case kLine_Verb:
    1112               0 :                     tmp.lineTo(pts[1]);
    1113               0 :                     break;
    1114                 :                 case kQuad_Verb:
    1115               0 :                     subdivide_quad_to(&tmp, pts);
    1116               0 :                     break;
    1117                 :                 case kCubic_Verb:
    1118               0 :                     subdivide_cubic_to(&tmp, pts);
    1119               0 :                     break;
    1120                 :                 case kClose_Verb:
    1121               0 :                     tmp.close();
    1122               0 :                     break;
    1123                 :                 default:
    1124               0 :                     SkDEBUGFAIL("unknown verb");
    1125               0 :                     break;
    1126                 :             }
    1127                 :         }
    1128                 : 
    1129               0 :         dst->swap(tmp);
    1130               0 :         matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
    1131                 :     } else {
    1132                 :         // remember that dst might == this, so be sure to check
    1133                 :         // fBoundsIsDirty before we set it
    1134               0 :         if (!fBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
    1135                 :             // if we're empty, fastbounds should not be mapped
    1136               0 :             matrix.mapRect(&dst->fBounds, fBounds);
    1137               0 :             dst->fBoundsIsDirty = false;
    1138                 :         } else {
    1139                 :             GEN_ID_PTR_INC(dst);
    1140               0 :             dst->fBoundsIsDirty = true;
    1141                 :         }
    1142                 : 
    1143               0 :         if (this != dst) {
    1144               0 :             dst->fVerbs = fVerbs;
    1145               0 :             dst->fPts.setCount(fPts.count());
    1146               0 :             dst->fFillType = fFillType;
    1147               0 :             dst->fSegmentMask = fSegmentMask;
    1148               0 :             dst->fConvexity = fConvexity;
    1149                 :         }
    1150               0 :         matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
    1151               0 :         SkDEBUGCODE(dst->validate();)
    1152                 :     }
    1153               0 : }
    1154                 : 
    1155                 : ///////////////////////////////////////////////////////////////////////////////
    1156                 : ///////////////////////////////////////////////////////////////////////////////
    1157                 : 
    1158                 : enum SegmentState {
    1159                 :     kAfterClose_SegmentState,     // We will need a move next, but we have a
    1160                 :                                   // previous close pt to use for the new move.
    1161                 :     kAfterMove_SegmentState,      // We have seen a move, but nothing else.
    1162                 :     kAfterPrimitive_SegmentState  // We have seen a primitive but not yet
    1163                 :                                   // closed the path. Also the initial state.
    1164                 : };
    1165                 : 
    1166               0 : SkPath::Iter::Iter() {
    1167                 : #ifdef SK_DEBUG
    1168               0 :     fPts = NULL;
    1169               0 :     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
    1170               0 :     fForceClose = fCloseLine = false;
    1171               0 :     fSegmentState = kAfterPrimitive_SegmentState;
    1172                 : #endif
    1173                 :     // need to init enough to make next() harmlessly return kDone_Verb
    1174               0 :     fVerbs = NULL;
    1175               0 :     fVerbStop = NULL;
    1176               0 :     fNeedClose = false;
    1177               0 : }
    1178                 : 
    1179               0 : SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
    1180               0 :     this->setPath(path, forceClose);
    1181               0 : }
    1182                 : 
    1183               0 : void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
    1184               0 :     fPts = path.fPts.begin();
    1185               0 :     fVerbs = path.fVerbs.begin();
    1186               0 :     fVerbStop = path.fVerbs.end();
    1187               0 :     fLastPt.fX = fLastPt.fY = 0;
    1188               0 :     fMoveTo.fX = fMoveTo.fY = 0;
    1189               0 :     fForceClose = SkToU8(forceClose);
    1190               0 :     fNeedClose = false;
    1191               0 :     fSegmentState = kAfterClose_SegmentState;
    1192               0 : }
    1193                 : 
    1194               0 : bool SkPath::Iter::isClosedContour() const {
    1195               0 :     if (fVerbs == NULL || fVerbs == fVerbStop) {
    1196               0 :         return false;
    1197                 :     }
    1198               0 :     if (fForceClose) {
    1199               0 :         return true;
    1200                 :     }
    1201                 : 
    1202               0 :     const uint8_t* verbs = fVerbs;
    1203               0 :     const uint8_t* stop = fVerbStop;
    1204                 : 
    1205               0 :     if (kMove_Verb == *verbs) {
    1206               0 :         verbs += 1; // skip the initial moveto
    1207                 :     }
    1208                 : 
    1209               0 :     while (verbs < stop) {
    1210               0 :         unsigned v = *verbs++;
    1211               0 :         if (kMove_Verb == v) {
    1212               0 :             break;
    1213                 :         }
    1214               0 :         if (kClose_Verb == v) {
    1215               0 :             return true;
    1216                 :         }
    1217                 :     }
    1218               0 :     return false;
    1219                 : }
    1220                 : 
    1221               0 : SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
    1222               0 :     if (fLastPt != fMoveTo) {
    1223                 :         // A special case: if both points are NaN, SkPoint::operation== returns
    1224                 :         // false, but the iterator expects that they are treated as the same.
    1225                 :         // (consider SkPoint is a 2-dimension float point).
    1226               0 :         if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
    1227               0 :             SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
    1228               0 :             return kClose_Verb;
    1229                 :         }
    1230                 : 
    1231               0 :         if (pts) {
    1232               0 :             pts[0] = fLastPt;
    1233               0 :             pts[1] = fMoveTo;
    1234                 :         }
    1235               0 :         fLastPt = fMoveTo;
    1236               0 :         fCloseLine = true;
    1237               0 :         return kLine_Verb;
    1238                 :     } else {
    1239               0 :         pts[0] = fMoveTo;
    1240               0 :         return kClose_Verb;
    1241                 :     }
    1242                 : }
    1243                 : 
    1244               0 : bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
    1245               0 :     if (fSegmentState == kAfterClose_SegmentState) {
    1246                 :         // We have closed a curve and have a primitive, so we need a move.
    1247                 :         // Set the first return pt to the most recent move pt
    1248               0 :         if (pts) {
    1249               0 :             *pts = fMoveTo;
    1250                 :         }
    1251               0 :         fNeedClose = fForceClose;
    1252               0 :         fSegmentState = kAfterMove_SegmentState;
    1253               0 :         fVerbs -= 1; // Step back to see the primitive again
    1254               0 :         return true;
    1255                 :     }
    1256                 : 
    1257               0 :     if (fSegmentState == kAfterMove_SegmentState) {
    1258                 :         // Set the first return pt to the move pt
    1259               0 :         if (pts) {
    1260               0 :             *pts = fMoveTo;
    1261                 :         }
    1262               0 :         fSegmentState = kAfterPrimitive_SegmentState;
    1263                 :     } else {
    1264               0 :         SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
    1265                 :          // Set the first return pt to the last pt of the previous primitive.
    1266               0 :         if (pts) {
    1267               0 :             *pts = fPts[-1];
    1268                 :         }
    1269                 :     }
    1270               0 :     return false;
    1271                 : }
    1272                 : 
    1273               0 : void SkPath::Iter::consumeDegenerateSegments() {
    1274                 :     // We need to step over anything that will not move the current draw point
    1275                 :     // forward before the next move is seen
    1276               0 :     const uint8_t* lastMoveVerb = 0;
    1277               0 :     const SkPoint* lastMovePt = 0;
    1278               0 :     SkPoint lastPt = fLastPt;
    1279               0 :     while (fVerbs != fVerbStop) {
    1280               0 :         unsigned verb = *fVerbs;
    1281               0 :         switch (verb) {
    1282                 :             case kMove_Verb:
    1283                 :                 // Keep a record of this most recent move
    1284               0 :                 lastMoveVerb = fVerbs;
    1285               0 :                 lastMovePt = fPts;
    1286               0 :                 lastPt = fPts[0];
    1287               0 :                 fVerbs++;
    1288               0 :                 fPts++;
    1289               0 :                 break;
    1290                 : 
    1291                 :             case kClose_Verb:
    1292                 :                 // A close when we are in a segment is always valid
    1293               0 :                 if (fSegmentState == kAfterPrimitive_SegmentState) {
    1294               0 :                     return;
    1295                 :                 }
    1296                 :                 // A close at any other time must be ignored
    1297               0 :                 fVerbs++;
    1298               0 :                 break;
    1299                 : 
    1300                 :             case kLine_Verb:
    1301               0 :                 if (!IsLineDegenerate(lastPt, fPts[0])) {
    1302               0 :                     if (lastMoveVerb) {
    1303               0 :                         fVerbs = lastMoveVerb;
    1304               0 :                         fPts = lastMovePt;
    1305               0 :                         return;
    1306                 :                     }
    1307               0 :                     return;
    1308                 :                 }
    1309                 :                 // Ignore this line and continue
    1310               0 :                 fVerbs++;
    1311               0 :                 fPts++;
    1312               0 :                 break;
    1313                 : 
    1314                 :             case kQuad_Verb:
    1315               0 :                 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) {
    1316               0 :                     if (lastMoveVerb) {
    1317               0 :                         fVerbs = lastMoveVerb;
    1318               0 :                         fPts = lastMovePt;
    1319               0 :                         return;
    1320                 :                     }
    1321               0 :                     return;
    1322                 :                 }
    1323                 :                 // Ignore this line and continue
    1324               0 :                 fVerbs++;
    1325               0 :                 fPts += 2;
    1326               0 :                 break;
    1327                 : 
    1328                 :             case kCubic_Verb:
    1329               0 :                 if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2])) {
    1330               0 :                     if (lastMoveVerb) {
    1331               0 :                         fVerbs = lastMoveVerb;
    1332               0 :                         fPts = lastMovePt;
    1333               0 :                         return;
    1334                 :                     }
    1335               0 :                     return;
    1336                 :                 }
    1337                 :                 // Ignore this line and continue
    1338               0 :                 fVerbs++;
    1339               0 :                 fPts += 3;
    1340               0 :                 break;
    1341                 : 
    1342                 :             default:
    1343               0 :                 SkDEBUGFAIL("Should never see kDone_Verb");
    1344                 :         }
    1345                 :     }
    1346                 : }
    1347                 : 
    1348               0 : SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
    1349                 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
    1350               0 :     this->consumeDegenerateSegments();
    1351                 : #endif
    1352                 : 
    1353               0 :     if (fVerbs == fVerbStop) {
    1354                 :         // Close the curve if requested and if there is some curve to close
    1355                 : #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
    1356                 :         if (fNeedClose) {
    1357                 : #else
    1358               0 :         if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
    1359                 : #endif
    1360               0 :             if (kLine_Verb == this->autoClose(pts)) {
    1361               0 :                 return kLine_Verb;
    1362                 :             }
    1363               0 :             fNeedClose = false;
    1364               0 :             return kClose_Verb;
    1365                 :         }
    1366               0 :         return kDone_Verb;
    1367                 :     }
    1368                 : 
    1369               0 :     unsigned        verb = *fVerbs++;
    1370               0 :     const SkPoint*  srcPts = fPts;
    1371                 : 
    1372               0 :     switch (verb) {
    1373                 :         case kMove_Verb:
    1374               0 :             if (fNeedClose) {
    1375               0 :                 fVerbs -= 1;
    1376               0 :                 verb = this->autoClose(pts);
    1377               0 :                 if (verb == kClose_Verb) {
    1378               0 :                     fNeedClose = false;
    1379                 :                 }
    1380               0 :                 return (Verb)verb;
    1381                 :             }
    1382               0 :             if (fVerbs == fVerbStop) {    // might be a trailing moveto
    1383               0 :                 return kDone_Verb;
    1384                 :             }
    1385               0 :             fMoveTo = *srcPts;
    1386               0 :             if (pts) {
    1387               0 :                 pts[0] = *srcPts;
    1388                 :             }
    1389               0 :             srcPts += 1;
    1390               0 :             fSegmentState = kAfterMove_SegmentState;
    1391                 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
    1392               0 :             fLastPt = fMoveTo;
    1393                 : #endif
    1394               0 :             fNeedClose = fForceClose;
    1395               0 :             break;
    1396                 :         case kLine_Verb:
    1397               0 :             if (this->cons_moveTo(pts)) {
    1398               0 :                 return kMove_Verb;
    1399                 :             }
    1400               0 :             if (pts) {
    1401               0 :                 pts[1] = srcPts[0];
    1402                 :             }
    1403               0 :             fLastPt = srcPts[0];
    1404               0 :             fCloseLine = false;
    1405               0 :             srcPts += 1;
    1406               0 :             break;
    1407                 :         case kQuad_Verb:
    1408               0 :             if (this->cons_moveTo(pts)) {
    1409               0 :                 return kMove_Verb;
    1410                 :             }
    1411               0 :             if (pts) {
    1412               0 :                 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
    1413                 :             }
    1414               0 :             fLastPt = srcPts[1];
    1415               0 :             srcPts += 2;
    1416               0 :             break;
    1417                 :         case kCubic_Verb:
    1418               0 :             if (this->cons_moveTo(pts)) {
    1419               0 :                 return kMove_Verb;
    1420                 :             }
    1421               0 :             if (pts) {
    1422               0 :                 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
    1423                 :             }
    1424               0 :             fLastPt = srcPts[2];
    1425               0 :             srcPts += 3;
    1426               0 :             break;
    1427                 :         case kClose_Verb:
    1428               0 :             verb = this->autoClose(pts);
    1429               0 :             if (verb == kLine_Verb) {
    1430               0 :                 fVerbs -= 1;
    1431                 :             } else {
    1432               0 :                 fNeedClose = false;
    1433                 : #ifndef SK_OLD_EMPTY_PATH_BEHAVIOR
    1434               0 :                 fSegmentState = kAfterClose_SegmentState;
    1435                 : #endif
    1436                 :             }
    1437                 : #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
    1438                 :             fSegmentState = kAfterClose_SegmentState;
    1439                 : #else
    1440               0 :             fLastPt = fMoveTo;
    1441                 : #endif
    1442               0 :             break;
    1443                 :     }
    1444               0 :     fPts = srcPts;
    1445               0 :     return (Verb)verb;
    1446                 : }
    1447                 : 
    1448                 : ///////////////////////////////////////////////////////////////////////////////
    1449                 : 
    1450               0 : SkPath::RawIter::RawIter() {
    1451                 : #ifdef SK_DEBUG
    1452               0 :     fPts = NULL;
    1453               0 :     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
    1454                 : #endif
    1455                 :     // need to init enough to make next() harmlessly return kDone_Verb
    1456               0 :     fVerbs = NULL;
    1457               0 :     fVerbStop = NULL;
    1458               0 : }
    1459                 : 
    1460               0 : SkPath::RawIter::RawIter(const SkPath& path) {
    1461               0 :     this->setPath(path);
    1462               0 : }
    1463                 : 
    1464               0 : void SkPath::RawIter::setPath(const SkPath& path) {
    1465               0 :     fPts = path.fPts.begin();
    1466               0 :     fVerbs = path.fVerbs.begin();
    1467               0 :     fVerbStop = path.fVerbs.end();
    1468               0 :     fMoveTo.fX = fMoveTo.fY = 0;
    1469               0 :     fLastPt.fX = fLastPt.fY = 0;
    1470               0 : }
    1471                 : 
    1472               0 : SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
    1473               0 :     if (fVerbs == fVerbStop) {
    1474               0 :         return kDone_Verb;
    1475                 :     }
    1476                 : 
    1477               0 :     unsigned        verb = *fVerbs++;
    1478               0 :     const SkPoint*  srcPts = fPts;
    1479                 : 
    1480               0 :     switch (verb) {
    1481                 :         case kMove_Verb:
    1482               0 :             if (pts) {
    1483               0 :                 pts[0] = *srcPts;
    1484                 :             }
    1485               0 :             fMoveTo = srcPts[0];
    1486               0 :             fLastPt = fMoveTo;
    1487               0 :             srcPts += 1;
    1488               0 :             break;
    1489                 :         case kLine_Verb:
    1490               0 :             if (pts) {
    1491               0 :                 pts[0] = fLastPt;
    1492               0 :                 pts[1] = srcPts[0];
    1493                 :             }
    1494               0 :             fLastPt = srcPts[0];
    1495               0 :             srcPts += 1;
    1496               0 :             break;
    1497                 :         case kQuad_Verb:
    1498               0 :             if (pts) {
    1499               0 :                 pts[0] = fLastPt;
    1500               0 :                 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
    1501                 :             }
    1502               0 :             fLastPt = srcPts[1];
    1503               0 :             srcPts += 2;
    1504               0 :             break;
    1505                 :         case kCubic_Verb:
    1506               0 :             if (pts) {
    1507               0 :                 pts[0] = fLastPt;
    1508               0 :                 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
    1509                 :             }
    1510               0 :             fLastPt = srcPts[2];
    1511               0 :             srcPts += 3;
    1512               0 :             break;
    1513                 :         case kClose_Verb:
    1514               0 :             fLastPt = fMoveTo;
    1515               0 :             if (pts) {
    1516               0 :                 pts[0] = fMoveTo;
    1517                 :             }
    1518               0 :             break;
    1519                 :     }
    1520               0 :     fPts = srcPts;
    1521               0 :     return (Verb)verb;
    1522                 : }
    1523                 : 
    1524                 : ///////////////////////////////////////////////////////////////////////////////
    1525                 : 
    1526                 : /*
    1527                 :     Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
    1528                 : */
    1529                 : 
    1530               0 : void SkPath::flatten(SkWriter32& buffer) const {
    1531               0 :     SkDEBUGCODE(this->validate();)
    1532                 : 
    1533               0 :     buffer.write32(fPts.count());
    1534               0 :     buffer.write32(fVerbs.count());
    1535               0 :     buffer.write32((fFillType << 8) | fSegmentMask);
    1536               0 :     buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
    1537               0 :     buffer.writePad(fVerbs.begin(), fVerbs.count());
    1538               0 : }
    1539                 : 
    1540               0 : void SkPath::unflatten(SkReader32& buffer) {
    1541               0 :     fPts.setCount(buffer.readS32());
    1542               0 :     fVerbs.setCount(buffer.readS32());
    1543               0 :     uint32_t packed = buffer.readS32();
    1544               0 :     fFillType = packed >> 8;
    1545               0 :     fSegmentMask = packed & 0xFF;
    1546               0 :     buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
    1547               0 :     buffer.read(fVerbs.begin(), fVerbs.count());
    1548                 : 
    1549                 :     GEN_ID_INC;
    1550               0 :     DIRTY_AFTER_EDIT;
    1551                 : 
    1552               0 :     SkDEBUGCODE(this->validate();)
    1553               0 : }
    1554                 : 
    1555                 : ///////////////////////////////////////////////////////////////////////////////
    1556                 : 
    1557               0 : void SkPath::dump(bool forceClose, const char title[]) const {
    1558               0 :     Iter    iter(*this, forceClose);
    1559                 :     SkPoint pts[4];
    1560                 :     Verb    verb;
    1561                 : 
    1562                 :     SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
    1563               0 :              title ? title : "");
    1564                 : 
    1565               0 :     while ((verb = iter.next(pts)) != kDone_Verb) {
    1566               0 :         switch (verb) {
    1567                 :             case kMove_Verb:
    1568                 : #ifdef SK_CAN_USE_FLOAT
    1569                 :                 SkDebugf("  path: moveTo [%g %g]\n",
    1570               0 :                         SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
    1571                 : #else
    1572                 :                 SkDebugf("  path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
    1573                 : #endif
    1574               0 :                 break;
    1575                 :             case kLine_Verb:
    1576                 : #ifdef SK_CAN_USE_FLOAT
    1577                 :                 SkDebugf("  path: lineTo [%g %g]\n",
    1578               0 :                         SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
    1579                 : #else
    1580                 :                 SkDebugf("  path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
    1581                 : #endif
    1582               0 :                 break;
    1583                 :             case kQuad_Verb:
    1584                 : #ifdef SK_CAN_USE_FLOAT
    1585                 :                 SkDebugf("  path: quadTo [%g %g] [%g %g]\n",
    1586                 :                         SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
    1587               0 :                         SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
    1588                 : #else
    1589                 :                 SkDebugf("  path: quadTo [%x %x] [%x %x]\n",
    1590                 :                          pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
    1591                 : #endif
    1592               0 :                 break;
    1593                 :             case kCubic_Verb:
    1594                 : #ifdef SK_CAN_USE_FLOAT
    1595                 :                 SkDebugf("  path: cubeTo [%g %g] [%g %g] [%g %g]\n",
    1596                 :                         SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
    1597                 :                         SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
    1598               0 :                         SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
    1599                 : #else
    1600                 :                 SkDebugf("  path: cubeTo [%x %x] [%x %x] [%x %x]\n",
    1601                 :                          pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
    1602                 :                          pts[3].fX, pts[3].fY);
    1603                 : #endif
    1604               0 :                 break;
    1605                 :             case kClose_Verb:
    1606               0 :                 SkDebugf("  path: close\n");
    1607               0 :                 break;
    1608                 :             default:
    1609               0 :                 SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
    1610               0 :                 verb = kDone_Verb;  // stop the loop
    1611               0 :                 break;
    1612                 :         }
    1613                 :     }
    1614               0 :     SkDebugf("path: done %s\n", title ? title : "");
    1615               0 : }
    1616                 : 
    1617               0 : void SkPath::dump() const {
    1618               0 :     this->dump(false);
    1619               0 : }
    1620                 : 
    1621                 : #ifdef SK_DEBUG
    1622               0 : void SkPath::validate() const {
    1623               0 :     SkASSERT(this != NULL);
    1624               0 :     SkASSERT((fFillType & ~3) == 0);
    1625               0 :     fPts.validate();
    1626               0 :     fVerbs.validate();
    1627                 : 
    1628               0 :     if (!fBoundsIsDirty) {
    1629                 :         SkRect bounds;
    1630               0 :         compute_pt_bounds(&bounds, fPts);
    1631               0 :         if (fPts.count() <= 1) {
    1632                 :             // if we're empty, fBounds may be empty but translated, so we can't
    1633                 :             // necessarily compare to bounds directly
    1634                 :             // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
    1635                 :             // be [2, 2, 2, 2]
    1636               0 :             SkASSERT(bounds.isEmpty());
    1637               0 :             SkASSERT(fBounds.isEmpty());
    1638                 :         } else {
    1639               0 :             if (bounds.isEmpty()) {
    1640               0 :                 SkASSERT(fBounds.isEmpty());
    1641                 :             } else {
    1642               0 :                 if (!fBounds.isEmpty()) {
    1643               0 :                     SkASSERT(fBounds.contains(bounds));
    1644                 :                 }
    1645                 :             }
    1646                 :         }
    1647                 :     }
    1648                 : 
    1649               0 :     uint32_t mask = 0;
    1650               0 :     for (int i = 0; i < fVerbs.count(); i++) {
    1651               0 :         switch (fVerbs[i]) {
    1652                 :             case kLine_Verb:
    1653               0 :                 mask |= kLine_SegmentMask;
    1654               0 :                 break;
    1655                 :             case kQuad_Verb:
    1656               0 :                 mask |= kQuad_SegmentMask;
    1657               0 :                 break;
    1658                 :             case kCubic_Verb:
    1659               0 :                 mask |= kCubic_SegmentMask;
    1660                 :         }
    1661                 :     }
    1662               0 :     SkASSERT(mask == fSegmentMask);
    1663               0 : }
    1664                 : #endif
    1665                 : 
    1666                 : ///////////////////////////////////////////////////////////////////////////////
    1667                 : 
    1668               0 : static int sign(SkScalar x) { return x < 0; }
    1669                 : #define kValueNeverReturnedBySign   2
    1670                 : 
    1671               0 : static int CrossProductSign(const SkVector& a, const SkVector& b) {
    1672               0 :     return SkScalarSignAsInt(SkPoint::CrossProduct(a, b));
    1673                 : }
    1674                 : 
    1675                 : // only valid for a single contour
    1676                 : struct Convexicator {
    1677               0 :     Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) {
    1678               0 :         fSign = 0;
    1679                 :         // warnings
    1680               0 :         fCurrPt.set(0, 0);
    1681               0 :         fVec0.set(0, 0);
    1682               0 :         fVec1.set(0, 0);
    1683               0 :         fFirstVec.set(0, 0);
    1684                 : 
    1685               0 :         fDx = fDy = 0;
    1686               0 :         fSx = fSy = kValueNeverReturnedBySign;
    1687               0 :     }
    1688                 : 
    1689               0 :     SkPath::Convexity getConvexity() const { return fConvexity; }
    1690                 : 
    1691               0 :     void addPt(const SkPoint& pt) {
    1692               0 :         if (SkPath::kConcave_Convexity == fConvexity) {
    1693               0 :             return;
    1694                 :         }
    1695                 : 
    1696               0 :         if (0 == fPtCount) {
    1697               0 :             fCurrPt = pt;
    1698               0 :             ++fPtCount;
    1699                 :         } else {
    1700               0 :             SkVector vec = pt - fCurrPt;
    1701               0 :             if (vec.fX || vec.fY) {
    1702               0 :                 fCurrPt = pt;
    1703               0 :                 if (++fPtCount == 2) {
    1704               0 :                     fFirstVec = fVec1 = vec;
    1705                 :                 } else {
    1706               0 :                     SkASSERT(fPtCount > 2);
    1707               0 :                     this->addVec(vec);
    1708                 :                 }
    1709                 :                 
    1710               0 :                 int sx = sign(vec.fX);
    1711               0 :                 int sy = sign(vec.fY);
    1712               0 :                 fDx += (sx != fSx);
    1713               0 :                 fDy += (sy != fSy);
    1714               0 :                 fSx = sx;
    1715               0 :                 fSy = sy;
    1716                 :                 
    1717               0 :                 if (fDx > 3 || fDy > 3) {
    1718               0 :                     fConvexity = SkPath::kConcave_Convexity;
    1719                 :                 }
    1720                 :             }
    1721                 :         }
    1722                 :     }
    1723                 : 
    1724               0 :     void close() {
    1725               0 :         if (fPtCount > 2) {
    1726               0 :             this->addVec(fFirstVec);
    1727                 :         }
    1728               0 :     }
    1729                 : 
    1730                 : private:
    1731               0 :     void addVec(const SkVector& vec) {
    1732               0 :         SkASSERT(vec.fX || vec.fY);
    1733               0 :         fVec0 = fVec1;
    1734               0 :         fVec1 = vec;
    1735               0 :         int sign = CrossProductSign(fVec0, fVec1);
    1736               0 :         if (0 == fSign) {
    1737               0 :             fSign = sign;
    1738               0 :         } else if (sign) {
    1739               0 :             if (fSign != sign) {
    1740               0 :                 fConvexity = SkPath::kConcave_Convexity;
    1741                 :             }
    1742                 :         }
    1743               0 :     }
    1744                 : 
    1745                 :     SkPoint             fCurrPt;
    1746                 :     SkVector            fVec0, fVec1, fFirstVec;
    1747                 :     int                 fPtCount;   // non-degenerate points
    1748                 :     int                 fSign;
    1749                 :     SkPath::Convexity   fConvexity;
    1750                 :     int                 fDx, fDy, fSx, fSy;
    1751                 : };
    1752                 : 
    1753               0 : SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
    1754                 :     SkPoint         pts[4];
    1755                 :     SkPath::Verb    verb;
    1756               0 :     SkPath::Iter    iter(path, true);
    1757                 : 
    1758               0 :     int             contourCount = 0;
    1759                 :     int             count;
    1760               0 :     Convexicator    state;
    1761                 : 
    1762               0 :     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
    1763               0 :         switch (verb) {
    1764                 :             case kMove_Verb:
    1765               0 :                 if (++contourCount > 1) {
    1766               0 :                     return kConcave_Convexity;
    1767                 :                 }
    1768               0 :                 pts[1] = pts[0];
    1769               0 :                 count = 1;
    1770               0 :                 break;
    1771               0 :             case kLine_Verb: count = 1; break;
    1772               0 :             case kQuad_Verb: count = 2; break;
    1773               0 :             case kCubic_Verb: count = 3; break;
    1774                 :             case kClose_Verb:
    1775               0 :                 state.close();
    1776               0 :                 count = 0;
    1777               0 :                 break;
    1778                 :             default:
    1779               0 :                 SkDEBUGFAIL("bad verb");
    1780               0 :                 return kConcave_Convexity;
    1781                 :         }
    1782                 : 
    1783               0 :         for (int i = 1; i <= count; i++) {
    1784               0 :             state.addPt(pts[i]);
    1785                 :         }
    1786                 :         // early exit
    1787               0 :         if (kConcave_Convexity == state.getConvexity()) {
    1788               0 :             return kConcave_Convexity;
    1789                 :         }
    1790                 :     }
    1791               0 :     return state.getConvexity();
    1792                 : }

Generated by: LCOV version 1.7