LCOV - app.info - gfx/skia/src/core/SkPath.cpp

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-07-07 | 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