Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2006 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef SkPath_DEFINED
      9 #define SkPath_DEFINED
     10 
     11 #include "SkMatrix.h"
     12 #include "SkPathRef.h"
     13 #include "SkRefCnt.h"
     14 
     15 class SkReader32;
     16 class SkWriter32;
     17 class SkAutoPathBoundsUpdate;
     18 class SkString;
     19 class SkRRect;
     20 class SkWStream;
     21 
     22 /** \class SkPath
     23 
     24     The SkPath class encapsulates compound (multiple contour) geometric paths
     25     consisting of straight line segments, quadratic curves, and cubic curves.
     26 
     27     SkPath is not thread safe unless you've first called SkPath::updateBoundsCache().
     28 */
     29 class SK_API SkPath {
     30 public:
     31     enum Direction {
     32         /** clockwise direction for adding closed contours */
     33         kCW_Direction,
     34         /** counter-clockwise direction for adding closed contours */
     35         kCCW_Direction,
     36     };
     37 
     38     SkPath();
     39     SkPath(const SkPath&);
     40     ~SkPath();
     41 
     42     SkPath& operator=(const SkPath&);
     43     friend  SK_API bool operator==(const SkPath&, const SkPath&);
     44     friend bool operator!=(const SkPath& a, const SkPath& b) {
     45         return !(a == b);
     46     }
     47 
     48     /** Return true if the paths contain an equal array of verbs and weights. Paths
     49      *  with equal verb counts can be readily interpolated. If the paths contain one
     50      *  or more conics, the conics' weights must also match.
     51      *
     52      *  @param compare  The path to compare.
     53      *
     54      *  @return true if the paths have the same verbs and weights.
     55      */
     56     bool isInterpolatable(const SkPath& compare) const;
     57 
     58     /** Interpolate between two paths with same-sized point arrays.
     59      *  The out path contains the verbs and weights of this path.
     60      *  The out points are a weighted average of this path and the ending path.
     61      *
     62      *  @param ending  The path to interpolate between.
     63      *  @param weight  The weight, from 0 to 1. The output points are set to
     64      *                 (this->points * weight) + ending->points * (1 - weight).
     65      *  @return true if the paths could be interpolated.
     66      */
     67     bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
     68 
     69 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     70     /** Returns true if the caller is the only owner of the underlying path data */
     71     bool unique() const { return fPathRef->unique(); }
     72 #endif
     73 
     74     enum FillType {
     75         /** Specifies that "inside" is computed by a non-zero sum of signed
     76             edge crossings
     77         */
     78         kWinding_FillType,
     79         /** Specifies that "inside" is computed by an odd number of edge
     80             crossings
     81         */
     82         kEvenOdd_FillType,
     83         /** Same as Winding, but draws outside of the path, rather than inside
     84         */
     85         kInverseWinding_FillType,
     86         /** Same as EvenOdd, but draws outside of the path, rather than inside
     87          */
     88         kInverseEvenOdd_FillType
     89     };
     90 
     91     /** Return the path's fill type. This is used to define how "inside" is
     92         computed. The default value is kWinding_FillType.
     93 
     94         @return the path's fill type
     95     */
     96     FillType getFillType() const { return (FillType)fFillType; }
     97 
     98     /** Set the path's fill type. This is used to define how "inside" is
     99         computed. The default value is kWinding_FillType.
    100 
    101         @param ft The new fill type for this path
    102     */
    103     void setFillType(FillType ft) {
    104         fFillType = SkToU8(ft);
    105     }
    106 
    107     /** Returns true if the filltype is one of the Inverse variants */
    108     bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
    109 
    110     /**
    111      *  Toggle between inverse and normal filltypes. This reverse the return
    112      *  value of isInverseFillType()
    113      */
    114     void toggleInverseFillType() {
    115         fFillType ^= 2;
    116     }
    117 
    118     enum Convexity {
    119         kUnknown_Convexity,
    120         kConvex_Convexity,
    121         kConcave_Convexity
    122     };
    123 
    124     /**
    125      *  Return the path's convexity, as stored in the path. If it is currently unknown,
    126      *  then this function will attempt to compute the convexity (and cache the result).
    127      */
    128     Convexity getConvexity() const {
    129         if (kUnknown_Convexity != fConvexity) {
    130             return static_cast<Convexity>(fConvexity);
    131         } else {
    132             return this->internalGetConvexity();
    133         }
    134     }
    135 
    136     /**
    137      *  Return the currently cached value for convexity, even if that is set to
    138      *  kUnknown_Convexity. Note: getConvexity() will automatically call
    139      *  ComputeConvexity and cache its return value if the current setting is
    140      *  kUnknown.
    141      */
    142     Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; }
    143 
    144     /**
    145      *  Store a convexity setting in the path. There is no automatic check to
    146      *  see if this value actually agrees with the return value that would be
    147      *  computed by getConvexity().
    148      *
    149      *  Note: even if this is set to a "known" value, if the path is later
    150      *  changed (e.g. lineTo(), addRect(), etc.) then the cached value will be
    151      *  reset to kUnknown_Convexity.
    152      */
    153     void setConvexity(Convexity);
    154 
    155     /**
    156      *  Returns true if the path is flagged as being convex. This is not a
    157      *  confirmed by any analysis, it is just the value set earlier.
    158      */
    159     bool isConvex() const {
    160         return kConvex_Convexity == this->getConvexity();
    161     }
    162 
    163     /**
    164      *  Set the isConvex flag to true or false. Convex paths may draw faster if
    165      *  this flag is set, though setting this to true on a path that is in fact
    166      *  not convex can give undefined results when drawn. Paths default to
    167      *  isConvex == false
    168      */
    169     SK_ATTR_DEPRECATED("use setConvexity")
    170     void setIsConvex(bool isConvex) {
    171         this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
    172     }
    173 
    174     /** Returns true if the path is an oval.
    175      *
    176      * @param rect      returns the bounding rect of this oval. It's a circle
    177      *                  if the height and width are the same.
    178      * @param dir       is the oval CCW (or CW if false).
    179      * @param start     indicates where the contour starts on the oval (see
    180      *                  SkPath::addOval for intepretation of the index).
    181      * @return true if this path is an oval.
    182      *              Tracking whether a path is an oval is considered an
    183      *              optimization for performance and so some paths that are in
    184      *              fact ovals can report false.
    185      */
    186     bool isOval(SkRect* rect, Direction* dir = nullptr,
    187                 unsigned* start = nullptr) const {
    188         bool isCCW = false;
    189         bool result = fPathRef->isOval(rect, &isCCW, start);
    190         if (dir && result) {
    191             *dir = isCCW ? kCCW_Direction : kCW_Direction;
    192         }
    193         return result;
    194     }
    195 
    196     /** Returns true if the path is a round rect.
    197      *
    198      * @param rrect  Returns the bounding rect and radii of this round rect.
    199      * @param dir    is the rrect CCW (or CW if false).
    200      * @param start  indicates where the contour starts on the rrect (see
    201      *               SkPath::addRRect for intepretation of the index).
    202      *
    203      * @return true if this path is a round rect.
    204      *              Tracking whether a path is a round rect is considered an
    205      *              optimization for performance and so some paths that are in
    206      *              fact round rects can report false.
    207      */
    208     bool isRRect(SkRRect* rrect, Direction* dir = nullptr,
    209                  unsigned* start = nullptr) const {
    210         bool isCCW = false;
    211         bool result = fPathRef->isRRect(rrect, &isCCW, start);
    212         if (dir && result) {
    213             *dir = isCCW ? kCCW_Direction : kCW_Direction;
    214         }
    215         return result;
    216     }
    217 
    218     /** Clear any lines and curves from the path, making it empty. This frees up
    219         internal storage associated with those segments.
    220         On Android, does not change fSourcePath.
    221     */
    222     void reset();
    223 
    224     /** Similar to reset(), in that all lines and curves are removed from the
    225         path. However, any internal storage for those lines/curves is retained,
    226         making reuse of the path potentially faster.
    227         On Android, does not change fSourcePath.
    228     */
    229     void rewind();
    230 
    231     /** Returns true if the path is empty (contains no lines or curves)
    232 
    233         @return true if the path is empty (contains no lines or curves)
    234     */
    235     bool isEmpty() const {
    236         SkDEBUGCODE(this->validate();)
    237         return 0 == fPathRef->countVerbs();
    238     }
    239 
    240     /** Return true if the last contour of this path ends with a close verb.
    241      */
    242     bool isLastContourClosed() const;
    243 
    244     /**
    245      *  Returns true if all of the points in this path are finite, meaning there
    246      *  are no infinities and no NaNs.
    247      */
    248     bool isFinite() const {
    249         SkDEBUGCODE(this->validate();)
    250         return fPathRef->isFinite();
    251     }
    252 
    253     /** Returns true if the path is volatile (i.e. should not be cached by devices.)
    254      */
    255     bool isVolatile() const {
    256         return SkToBool(fIsVolatile);
    257     }
    258 
    259     /** Specify whether this path is volatile. Paths are not volatile by
    260      default. Temporary paths that are discarded or modified after use should be
    261      marked as volatile. This provides a hint to the device that the path
    262      should not be cached. Providing this hint when appropriate can
    263      improve performance by avoiding unnecessary overhead and resource
    264      consumption on the device.
    265      */
    266     void setIsVolatile(bool isVolatile) {
    267         fIsVolatile = isVolatile;
    268     }
    269 
    270     /** Test a line for zero length
    271 
    272         @return true if the line is of zero length; otherwise false.
    273     */
    274     static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
    275         return exact ? p1 == p2 : p1.equalsWithinTolerance(p2);
    276     }
    277 
    278     /** Test a quad for zero length
    279 
    280         @return true if the quad is of zero length; otherwise false.
    281     */
    282     static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
    283                                  const SkPoint& p3, bool exact) {
    284         return exact ? p1 == p2 && p2 == p3 : p1.equalsWithinTolerance(p2) &&
    285                p2.equalsWithinTolerance(p3);
    286     }
    287 
    288     /** Test a cubic curve for zero length
    289 
    290         @return true if the cubic is of zero length; otherwise false.
    291     */
    292     static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
    293                                   const SkPoint& p3, const SkPoint& p4, bool exact) {
    294         return exact ? p1 == p2 && p2 == p3 && p3 == p4 : p1.equalsWithinTolerance(p2) &&
    295                p2.equalsWithinTolerance(p3) &&
    296                p3.equalsWithinTolerance(p4);
    297     }
    298 
    299     /**
    300      *  Returns true if the path specifies a single line (i.e. it contains just
    301      *  a moveTo and a lineTo). If so, and line[] is not null, it sets the 2
    302      *  points in line[] to the end-points of the line. If the path is not a
    303      *  line, returns false and ignores line[].
    304      */
    305     bool isLine(SkPoint line[2]) const;
    306 
    307     /** Return the number of points in the path
    308      */
    309     int countPoints() const;
    310 
    311     /** Return the point at the specified index. If the index is out of range
    312          (i.e. is not 0 <= index < countPoints()) then the returned coordinates
    313          will be (0,0)
    314      */
    315     SkPoint getPoint(int index) const;
    316 
    317     /** Returns the number of points in the path. Up to max points are copied.
    318 
    319         @param points If not null, receives up to max points
    320         @param max The maximum number of points to copy into points
    321         @return the actual number of points in the path
    322     */
    323     int getPoints(SkPoint points[], int max) const;
    324 
    325     /** Return the number of verbs in the path
    326      */
    327     int countVerbs() const;
    328 
    329     /** Returns the number of verbs in the path. Up to max verbs are copied. The
    330         verbs are copied as one byte per verb.
    331 
    332         @param verbs If not null, receives up to max verbs
    333         @param max The maximum number of verbs to copy into verbs
    334         @return the actual number of verbs in the path
    335     */
    336     int getVerbs(uint8_t verbs[], int max) const;
    337 
    338     //! Swap contents of this and other. Guaranteed not to throw
    339     void swap(SkPath& other);
    340 
    341     /**
    342      *  Returns the bounds of the path's points. If the path contains zero points/verbs, this
    343      *  will return the "empty" rect [0, 0, 0, 0].
    344      *  Note: this bounds may be larger than the actual shape, since curves
    345      *  do not extend as far as their control points. Additionally this bound encompases all points,
    346      *  even isolated moveTos either preceeding or following the last non-degenerate contour.
    347     */
    348     const SkRect& getBounds() const {
    349         return fPathRef->getBounds();
    350     }
    351 
    352     /** Calling this will, if the internal cache of the bounds is out of date,
    353         update it so that subsequent calls to getBounds will be instantaneous.
    354         This also means that any copies or simple transformations of the path
    355         will inherit the cached bounds.
    356      */
    357     void updateBoundsCache() const {
    358         // for now, just calling getBounds() is sufficient
    359         this->getBounds();
    360     }
    361 
    362     /**
    363      *  Computes a bounds that is conservatively "snug" around the path. This assumes that the
    364      *  path will be filled. It does not attempt to collapse away contours that are logically
    365      *  empty (e.g. moveTo(x, y) + lineTo(x, y)) but will include them in the calculation.
    366      *
    367      *  It differs from getBounds() in that it will look at the snug bounds of curves, whereas
    368      *  getBounds() just returns the bounds of the control-points. Thus computing this may be
    369      *  slower than just calling getBounds().
    370      *
    371      *  If the path is empty (i.e. no points or verbs), it will return SkRect::MakeEmpty().
    372      */
    373     SkRect computeTightBounds() const;
    374 
    375     /**
    376      * Does a conservative test to see whether a rectangle is inside a path. Currently it only
    377      * will ever return true for single convex contour paths. The empty-status of the rect is not
    378      * considered (e.g. a rect that is a point can be inside a path). Points or line segments where
    379      * the rect edge touches the path border are not considered containment violations.
    380      */
    381     bool conservativelyContainsRect(const SkRect& rect) const;
    382 
    383     //  Construction methods
    384 
    385     /** Hint to the path to prepare for adding more points. This can allow the
    386         path to more efficiently grow its storage.
    387 
    388         @param extraPtCount The number of extra points the path should
    389                             preallocate for.
    390     */
    391     void incReserve(unsigned extraPtCount);
    392 
    393     /** Set the beginning of the next contour to the point (x,y).
    394 
    395         @param x    The x-coordinate of the start of a new contour
    396         @param y    The y-coordinate of the start of a new contour
    397     */
    398     void moveTo(SkScalar x, SkScalar y);
    399 
    400     /** Set the beginning of the next contour to the point
    401 
    402         @param p    The start of a new contour
    403     */
    404     void moveTo(const SkPoint& p) {
    405         this->moveTo(p.fX, p.fY);
    406     }
    407 
    408     /** Set the beginning of the next contour relative to the last point on the
    409         previous contour. If there is no previous contour, this is treated the
    410         same as moveTo().
    411 
    412         @param dx   The amount to add to the x-coordinate of the end of the
    413                     previous contour, to specify the start of a new contour
    414         @param dy   The amount to add to the y-coordinate of the end of the
    415                     previous contour, to specify the start of a new contour
    416     */
    417     void rMoveTo(SkScalar dx, SkScalar dy);
    418 
    419     /** Add a line from the last point to the specified point (x,y). If no
    420         moveTo() call has been made for this contour, the first point is
    421         automatically set to (0,0).
    422 
    423         @param x    The x-coordinate of the end of a line
    424         @param y    The y-coordinate of the end of a line
    425     */
    426     void lineTo(SkScalar x, SkScalar y);
    427 
    428     /** Add a line from the last point to the specified point. If no moveTo()
    429         call has been made for this contour, the first point is automatically
    430         set to (0,0).
    431 
    432         @param p    The end of a line
    433     */
    434     void lineTo(const SkPoint& p) {
    435         this->lineTo(p.fX, p.fY);
    436     }
    437 
    438     /** Same as lineTo, but the coordinates are considered relative to the last
    439         point on this contour. If there is no previous point, then a moveTo(0,0)
    440         is inserted automatically.
    441 
    442         @param dx   The amount to add to the x-coordinate of the previous point
    443                     on this contour, to specify a line
    444         @param dy   The amount to add to the y-coordinate of the previous point
    445                     on this contour, to specify a line
    446     */
    447     void rLineTo(SkScalar dx, SkScalar dy);
    448 
    449     /** Add a quadratic bezier from the last point, approaching control point
    450         (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
    451         this contour, the first point is automatically set to (0,0).
    452 
    453         @param x1   The x-coordinate of the control point on a quadratic curve
    454         @param y1   The y-coordinate of the control point on a quadratic curve
    455         @param x2   The x-coordinate of the end point on a quadratic curve
    456         @param y2   The y-coordinate of the end point on a quadratic curve
    457     */
    458     void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
    459 
    460     /** Add a quadratic bezier from the last point, approaching control point
    461         p1, and ending at p2. If no moveTo() call has been made for this
    462         contour, the first point is automatically set to (0,0).
    463 
    464         @param p1   The control point on a quadratic curve
    465         @param p2   The end point on a quadratic curve
    466     */
    467     void quadTo(const SkPoint& p1, const SkPoint& p2) {
    468         this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
    469     }
    470 
    471     /** Same as quadTo, but the coordinates are considered relative to the last
    472         point on this contour. If there is no previous point, then a moveTo(0,0)
    473         is inserted automatically.
    474 
    475         @param dx1   The amount to add to the x-coordinate of the last point on
    476                 this contour, to specify the control point of a quadratic curve
    477         @param dy1   The amount to add to the y-coordinate of the last point on
    478                 this contour, to specify the control point of a quadratic curve
    479         @param dx2   The amount to add to the x-coordinate of the last point on
    480                      this contour, to specify the end point of a quadratic curve
    481         @param dy2   The amount to add to the y-coordinate of the last point on
    482                      this contour, to specify the end point of a quadratic curve
    483     */
    484     void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
    485 
    486     void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    487                  SkScalar w);
    488     void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
    489         this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
    490     }
    491     void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
    492                   SkScalar w);
    493 
    494     /** Add a cubic bezier from the last point, approaching control points
    495         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
    496         made for this contour, the first point is automatically set to (0,0).
    497 
    498         @param x1   The x-coordinate of the 1st control point on a cubic curve
    499         @param y1   The y-coordinate of the 1st control point on a cubic curve
    500         @param x2   The x-coordinate of the 2nd control point on a cubic curve
    501         @param y2   The y-coordinate of the 2nd control point on a cubic curve
    502         @param x3   The x-coordinate of the end point on a cubic curve
    503         @param y3   The y-coordinate of the end point on a cubic curve
    504     */
    505     void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    506                  SkScalar x3, SkScalar y3);
    507 
    508     /** Add a cubic bezier from the last point, approaching control points p1
    509         and p2, and ending at p3. If no moveTo() call has been made for this
    510         contour, the first point is automatically set to (0,0).
    511 
    512         @param p1   The 1st control point on a cubic curve
    513         @param p2   The 2nd control point on a cubic curve
    514         @param p3   The end point on a cubic curve
    515     */
    516     void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
    517         this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
    518     }
    519 
    520     /** Same as cubicTo, but the coordinates are considered relative to the
    521         current point on this contour. If there is no previous point, then a
    522         moveTo(0,0) is inserted automatically.
    523 
    524         @param dx1   The amount to add to the x-coordinate of the last point on
    525                 this contour, to specify the 1st control point of a cubic curve
    526         @param dy1   The amount to add to the y-coordinate of the last point on
    527                 this contour, to specify the 1st control point of a cubic curve
    528         @param dx2   The amount to add to the x-coordinate of the last point on
    529                 this contour, to specify the 2nd control point of a cubic curve
    530         @param dy2   The amount to add to the y-coordinate of the last point on
    531                 this contour, to specify the 2nd control point of a cubic curve
    532         @param dx3   The amount to add to the x-coordinate of the last point on
    533                      this contour, to specify the end point of a cubic curve
    534         @param dy3   The amount to add to the y-coordinate of the last point on
    535                      this contour, to specify the end point of a cubic curve
    536     */
    537     void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    538                   SkScalar x3, SkScalar y3);
    539 
    540     /**
    541      *  Append the specified arc to the path. If the start of the arc is different from the path's
    542      *  current last point, then an automatic lineTo() is added to connect the current contour
    543      *  to the start of the arc. However, if the path is empty, then we call moveTo() with
    544      *  the first point of the arc. The sweep angle is treated mod 360.
    545      *
    546      *  @param oval The bounding oval defining the shape and size of the arc
    547      *  @param startAngle Starting angle (in degrees) where the arc begins
    548      *  @param sweepAngle Sweep angle (in degrees) measured clockwise. This is treated mod 360.
    549      *  @param forceMoveTo If true, always begin a new contour with the arc
    550      */
    551     void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo);
    552 
    553     /**
    554      *  Append a line and arc to the current path. This is the same as the PostScript call "arct".
    555      */
    556     void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius);
    557 
    558     /** Append a line and arc to the current path. This is the same as the
    559         PostScript call "arct".
    560     */
    561     void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
    562         this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
    563     }
    564 
    565     enum ArcSize {
    566         /** the smaller of the two possible SVG arcs. */
    567         kSmall_ArcSize,
    568         /** the larger of the two possible SVG arcs. */
    569         kLarge_ArcSize,
    570     };
    571 
    572     /**
    573      *  Append an elliptical arc from the current point in the format used by SVG.
    574      *  The center of the ellipse is computed to satisfy the constraints below.
    575      *
    576      *  @param rx,ry The radii in the x and y directions respectively.
    577      *  @param xAxisRotate The angle in degrees relative to the x-axis.
    578      *  @param largeArc Determines whether the smallest or largest arc possible
    579      *         is drawn.
    580      *  @param sweep Determines if the arc should be swept in an anti-clockwise or
    581      *         clockwise direction. Note that this enum value is opposite the SVG
    582      *         arc sweep value.
    583      *  @param x,y The destination coordinates.
    584      */
    585     void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
    586                Direction sweep, SkScalar x, SkScalar y);
    587 
    588     void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
    589                const SkPoint xy) {
    590         this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY);
    591     }
    592 
    593     /** Same as arcTo format used by SVG, but the destination coordinate is relative to the
    594      *  last point on this contour. If there is no previous point, then a
    595      *  moveTo(0,0) is inserted automatically.
    596      *
    597      *  @param rx,ry The radii in the x and y directions respectively.
    598      *  @param xAxisRotate The angle in degrees relative to the x-axis.
    599      *  @param largeArc Determines whether the smallest or largest arc possible
    600      *         is drawn.
    601      *  @param sweep Determines if the arc should be swept in an anti-clockwise or
    602      *         clockwise direction. Note that this enum value is opposite the SVG
    603      *         arc sweep value.
    604      *  @param dx,dy The destination coordinates relative to the last point.
    605      */
    606     void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
    607                 Direction sweep, SkScalar dx, SkScalar dy);
    608 
    609     /** Close the current contour. If the current point is not equal to the
    610         first point of the contour, a line segment is automatically added.
    611     */
    612     void close();
    613 
    614     /**
    615      *  Returns whether or not a fill type is inverted
    616      *
    617      *  kWinding_FillType        -> false
    618      *  kEvenOdd_FillType        -> false
    619      *  kInverseWinding_FillType -> true
    620      *  kInverseEvenOdd_FillType -> true
    621      */
    622     static bool IsInverseFillType(FillType fill) {
    623         static_assert(0 == kWinding_FillType, "fill_type_mismatch");
    624         static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
    625         static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
    626         static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
    627         return (fill & 2) != 0;
    628     }
    629 
    630     /**
    631      *  Returns the equivalent non-inverted fill type to the given fill type
    632      *
    633      *  kWinding_FillType        -> kWinding_FillType
    634      *  kEvenOdd_FillType        -> kEvenOdd_FillType
    635      *  kInverseWinding_FillType -> kWinding_FillType
    636      *  kInverseEvenOdd_FillType -> kEvenOdd_FillType
    637      */
    638     static FillType ConvertToNonInverseFillType(FillType fill) {
    639         static_assert(0 == kWinding_FillType, "fill_type_mismatch");
    640         static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
    641         static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
    642         static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
    643         return (FillType)(fill & 1);
    644     }
    645 
    646     /**
    647      *  Chop a conic into N quads, stored continguously in pts[], where
    648      *  N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
    649      */
    650     static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
    651                                    SkScalar w, SkPoint pts[], int pow2);
    652 
    653     /**
    654      *  Returns true if the path specifies a rectangle.
    655      *
    656      *  If this returns false, then all output parameters are ignored, and left
    657      *  unchanged. If this returns true, then each of the output parameters
    658      *  are checked for NULL. If they are not, they return their value.
    659      *
    660      *  @param rect If not null, set to the bounds of the rectangle.
    661      *              Note : this bounds may be smaller than the path's bounds, since it is just
    662      *              the bounds of the "drawable" parts of the path. e.g. a trailing MoveTo would
    663      *              be ignored in this rect, but not by the path's bounds
    664      *  @param isClosed If not null, set to true if the path is closed
    665      *  @param direction If not null, set to the rectangle's direction
    666      *  @return true if the path specifies a rectangle
    667      */
    668     bool isRect(SkRect* rect, bool* isClosed = NULL, Direction* direction = NULL) const;
    669 
    670     /** Returns true if the path specifies a pair of nested rectangles, or would draw a
    671         pair of nested rectangles when filled. If so, and if
    672         rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner
    673         rectangle. If so, and dirs is not null, set dirs[0] to the direction of
    674         the outer rectangle and dirs[1] to the direction of the inner rectangle. If
    675         the path does not specify a pair of nested rectangles, return
    676         false and ignore rect and dirs.
    677 
    678         @param rect If not null, returns the path as a pair of nested rectangles
    679         @param dirs If not null, returns the direction of the rects
    680         @return true if the path describes a pair of nested rectangles
    681     */
    682     bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = NULL) const;
    683 
    684     /**
    685      *  Add a closed rectangle contour to the path
    686      *  @param rect The rectangle to add as a closed contour to the path
    687      *  @param dir  The direction to wind the rectangle's contour.
    688      *
    689      *  Note: the contour initial point index is 0 (as defined below).
    690      */
    691     void addRect(const SkRect& rect, Direction dir = kCW_Direction);
    692 
    693     /**
    694      *  Add a closed rectangle contour to the path
    695      *  @param rect  The rectangle to add as a closed contour to the path
    696      *  @param dir   The direction to wind the rectangle's contour.
    697      *  @param start Initial point of the contour (initial moveTo), expressed as
    698      *               a corner index, starting in the upper-left position, clock-wise:
    699      *
    700      *  0         1
    701      *   *-------*
    702      *   |       |
    703      *   *-------*
    704      *  3         2
    705      */
    706     void addRect(const SkRect& rect, Direction dir, unsigned start);
    707 
    708     /**
    709      *  Add a closed rectangle contour to the path
    710      *
    711      *  @param left     The left side of a rectangle to add as a closed contour
    712      *                  to the path
    713      *  @param top      The top of a rectangle to add as a closed contour to the
    714      *                  path
    715      *  @param right    The right side of a rectangle to add as a closed contour
    716      *                  to the path
    717      *  @param bottom   The bottom of a rectangle to add as a closed contour to
    718      *                  the path
    719      *  @param dir  The direction to wind the rectangle's contour.
    720      *
    721      *  Note: the contour initial point index is 0 (as defined above).
    722      */
    723     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
    724                  Direction dir = kCW_Direction);
    725 
    726     /**
    727      *  Add a closed oval contour to the path
    728      *
    729      *  @param oval The bounding oval to add as a closed contour to the path
    730      *  @param dir  The direction to wind the oval's contour.
    731      *
    732      *  Note: the contour initial point index is 1 (as defined below).
    733      */
    734     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
    735 
    736     /**
    737      *  Add a closed oval contour to the path
    738      *
    739      *  @param oval  The bounding oval to add as a closed contour to the path
    740      *  @param dir   The direction to wind the oval's contour.
    741      *  @param start Initial point of the contour (initial moveTo), expressed
    742      *               as an ellipse vertex index, starting at the top, clock-wise
    743      *               (90/0/270/180deg order):
    744      *
    745      *        0
    746      *       -*-
    747      *     |     |
    748      *   3 *     * 1
    749      *     |     |
    750      *       -*-
    751      *        2
    752      */
    753     void addOval(const SkRect& oval, Direction dir, unsigned start);
    754 
    755     /**
    756      *  Add a closed circle contour to the path. The circle contour begins at
    757      *  the right-most point (as though 1 were passed to addOval's 'start' param).
    758      *
    759      *  @param x        The x-coordinate of the center of a circle to add as a
    760      *                  closed contour to the path
    761      *  @param y        The y-coordinate of the center of a circle to add as a
    762      *                  closed contour to the path
    763      *  @param radius   The radius of a circle to add as a closed contour to the
    764      *                  path
    765      *  @param dir  The direction to wind the circle's contour.
    766      */
    767     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
    768                    Direction dir = kCW_Direction);
    769 
    770     /** Add the specified arc to the path as a new contour.
    771 
    772         @param oval The bounds of oval used to define the size of the arc
    773         @param startAngle Starting angle (in degrees) where the arc begins
    774         @param sweepAngle Sweep angle (in degrees) measured clockwise
    775     */
    776     void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
    777 
    778     /**
    779      *  Add a closed round-rectangle contour to the path
    780      *  @param rect The bounds of a round-rectangle to add as a closed contour
    781      *  @param rx   The x-radius of the rounded corners on the round-rectangle
    782      *  @param ry   The y-radius of the rounded corners on the round-rectangle
    783      *  @param dir  The direction to wind the rectangle's contour.
    784      */
    785     void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
    786                       Direction dir = kCW_Direction);
    787 
    788     /**
    789      *  Add a closed round-rectangle contour to the path. Each corner receives
    790      *  two radius values [X, Y]. The corners are ordered top-left, top-right,
    791      *  bottom-right, bottom-left.
    792      *  @param rect The bounds of a round-rectangle to add as a closed contour
    793      *  @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
    794      *  @param dir  The direction to wind the rectangle's contour.
    795      * Note: The radii here now go through the same constraint handling as the
    796      *       SkRRect radii (i.e., either radii at a corner being 0 implies a
    797      *       sqaure corner and oversized radii are proportionally scaled down).
    798      */
    799     void addRoundRect(const SkRect& rect, const SkScalar radii[],
    800                       Direction dir = kCW_Direction);
    801 
    802     /**
    803      *  Add an SkRRect contour to the path
    804      *  @param rrect The rounded rect to add as a closed contour
    805      *  @param dir   The winding direction for the new contour.
    806      *
    807      *  Note: the contour initial point index is either 6 (for dir == kCW_Direction)
    808      *        or 7 (for dir == kCCW_Direction), as defined below.
    809      *
    810      */
    811     void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
    812 
    813     /**
    814      *  Add an SkRRect contour to the path
    815      *  @param rrect The rounded rect to add as a closed contour
    816      *  @param dir   The winding direction for the new contour.
    817      *  @param start Initial point of the contour (initial moveTo), expressed as
    818      *               an index of the radii minor/major points, ordered clock-wise:
    819      *
    820      *      0    1
    821      *      *----*
    822      *   7 *      * 2
    823      *     |      |
    824      *   6 *      * 3
    825      *      *----*
    826      *      5    4
    827      */
    828     void addRRect(const SkRRect& rrect, Direction dir, unsigned start);
    829 
    830     /**
    831      *  Add a new contour made of just lines. This is just a fast version of
    832      *  the following:
    833      *      this->moveTo(pts[0]);
    834      *      for (int i = 1; i < count; ++i) {
    835      *          this->lineTo(pts[i]);
    836      *      }
    837      *      if (close) {
    838      *          this->close();
    839      *      }
    840      */
    841     void addPoly(const SkPoint pts[], int count, bool close);
    842 
    843     enum AddPathMode {
    844         /** Source path contours are added as new contours.
    845         */
    846         kAppend_AddPathMode,
    847         /** Path is added by extending the last contour of the destination path
    848             with the first contour of the source path. If the last contour of
    849             the destination path is closed, then it will not be extended.
    850             Instead, the start of source path will be extended by a straight
    851             line to the end point of the destination path.
    852         */
    853         kExtend_AddPathMode
    854     };
    855 
    856     /** Add a copy of src to the path, offset by (dx,dy)
    857         @param src  The path to add as a new contour
    858         @param dx   The amount to translate the path in X as it is added
    859         @param dx   The amount to translate the path in Y as it is added
    860     */
    861     void addPath(const SkPath& src, SkScalar dx, SkScalar dy,
    862                  AddPathMode mode = kAppend_AddPathMode);
    863 
    864     /** Add a copy of src to the path
    865     */
    866     void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) {
    867         SkMatrix m;
    868         m.reset();
    869         this->addPath(src, m, mode);
    870     }
    871 
    872     /** Add a copy of src to the path, transformed by matrix
    873         @param src  The path to add as a new contour
    874         @param matrix  Transform applied to src
    875         @param mode  Determines how path is added
    876     */
    877     void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode);
    878 
    879     /**
    880      *  Same as addPath(), but reverses the src input
    881      */
    882     void reverseAddPath(const SkPath& src);
    883 
    884     /** Offset the path by (dx,dy), returning true on success
    885 
    886         @param dx   The amount in the X direction to offset the entire path
    887         @param dy   The amount in the Y direction to offset the entire path
    888         @param dst  The translated path is written here
    889     */
    890     void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
    891 
    892     /** Offset the path by (dx,dy), returning true on success
    893 
    894         @param dx   The amount in the X direction to offset the entire path
    895         @param dy   The amount in the Y direction to offset the entire path
    896     */
    897     void offset(SkScalar dx, SkScalar dy) {
    898         this->offset(dx, dy, this);
    899     }
    900 
    901     /** Transform the points in this path by matrix, and write the answer into
    902         dst.
    903 
    904         @param matrix   The matrix to apply to the path
    905         @param dst      The transformed path is written here
    906     */
    907     void transform(const SkMatrix& matrix, SkPath* dst) const;
    908 
    909     /** Transform the points in this path by matrix
    910 
    911         @param matrix The matrix to apply to the path
    912     */
    913     void transform(const SkMatrix& matrix) {
    914         this->transform(matrix, this);
    915     }
    916 
    917     /** Return the last point on the path. If no points have been added, (0,0)
    918         is returned. If there are no points, this returns false, otherwise it
    919         returns true.
    920 
    921         @param lastPt   The last point on the path is returned here
    922     */
    923     bool getLastPt(SkPoint* lastPt) const;
    924 
    925     /** Set the last point on the path. If no points have been added,
    926         moveTo(x,y) is automatically called.
    927 
    928         @param x    The new x-coordinate for the last point
    929         @param y    The new y-coordinate for the last point
    930     */
    931     void setLastPt(SkScalar x, SkScalar y);
    932 
    933     /** Set the last point on the path. If no points have been added, moveTo(p)
    934         is automatically called.
    935 
    936         @param p    The new location for the last point
    937     */
    938     void setLastPt(const SkPoint& p) {
    939         this->setLastPt(p.fX, p.fY);
    940     }
    941 
    942     enum SegmentMask {
    943         kLine_SegmentMask   = 1 << 0,
    944         kQuad_SegmentMask   = 1 << 1,
    945         kConic_SegmentMask  = 1 << 2,
    946         kCubic_SegmentMask  = 1 << 3,
    947     };
    948 
    949     /**
    950      *  Returns a mask, where each bit corresponding to a SegmentMask is
    951      *  set if the path contains 1 or more segments of that type.
    952      *  Returns 0 for an empty path (no segments).
    953      */
    954     uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); }
    955 
    956     enum Verb {
    957         kMove_Verb,     //!< iter.next returns 1 point
    958         kLine_Verb,     //!< iter.next returns 2 points
    959         kQuad_Verb,     //!< iter.next returns 3 points
    960         kConic_Verb,    //!< iter.next returns 3 points + iter.conicWeight()
    961         kCubic_Verb,    //!< iter.next returns 4 points
    962         kClose_Verb,    //!< iter.next returns 0 points
    963         kDone_Verb,     //!< iter.next returns 0 points
    964     };
    965 
    966     /** Iterate through all of the segments (lines, quadratics, cubics) of
    967         each contours in a path.
    968 
    969         The iterator cleans up the segments along the way, removing degenerate
    970         segments and adding close verbs where necessary. When the forceClose
    971         argument is provided, each contour (as defined by a new starting
    972         move command) will be completed with a close verb regardless of the
    973         contour's contents.
    974     */
    975     class SK_API Iter {
    976     public:
    977         Iter();
    978         Iter(const SkPath&, bool forceClose);
    979 
    980         void setPath(const SkPath&, bool forceClose);
    981 
    982         /** Return the next verb in this iteration of the path. When all
    983             segments have been visited, return kDone_Verb.
    984 
    985             @param  pts The points representing the current verb and/or segment
    986             @param doConsumeDegerates If true, first scan for segments that are
    987                    deemed degenerate (too short) and skip those.
    988             @param exact if doConsumeDegenerates is true and exact is true, skip only
    989                    degenerate elements with lengths exactly equal to zero. If exact
    990                    is false, skip degenerate elements with lengths close to zero. If
    991                    doConsumeDegenerates is false, exact has no effect.
    992             @return The verb for the current segment
    993         */
    994         Verb next(SkPoint pts[4], bool doConsumeDegerates = true, bool exact = false) {
    995             if (doConsumeDegerates) {
    996                 this->consumeDegenerateSegments(exact);
    997             }
    998             return this->doNext(pts);
    999         }
   1000 
   1001         /**
   1002          *  Return the weight for the current conic. Only valid if the current
   1003          *  segment return by next() was a conic.
   1004          */
   1005         SkScalar conicWeight() const { return *fConicWeights; }
   1006 
   1007         /** If next() returns kLine_Verb, then this query returns true if the
   1008             line was the result of a close() command (i.e. the end point is the
   1009             initial moveto for this contour). If next() returned a different
   1010             verb, this returns an undefined value.
   1011 
   1012             @return If the last call to next() returned kLine_Verb, return true
   1013                     if it was the result of an explicit close command.
   1014         */
   1015         bool isCloseLine() const { return SkToBool(fCloseLine); }
   1016 
   1017         /** Returns true if the current contour is closed (has a kClose_Verb)
   1018             @return true if the current contour is closed (has a kClose_Verb)
   1019         */
   1020         bool isClosedContour() const;
   1021 
   1022     private:
   1023         const SkPoint*  fPts;
   1024         const uint8_t*  fVerbs;
   1025         const uint8_t*  fVerbStop;
   1026         const SkScalar* fConicWeights;
   1027         SkPoint         fMoveTo;
   1028         SkPoint         fLastPt;
   1029         SkBool8         fForceClose;
   1030         SkBool8         fNeedClose;
   1031         SkBool8         fCloseLine;
   1032         SkBool8         fSegmentState;
   1033 
   1034         inline const SkPoint& cons_moveTo();
   1035         Verb autoClose(SkPoint pts[2]);
   1036         void consumeDegenerateSegments(bool exact);
   1037         Verb doNext(SkPoint pts[4]);
   1038     };
   1039 
   1040     /** Iterate through the verbs in the path, providing the associated points.
   1041     */
   1042     class SK_API RawIter {
   1043     public:
   1044         RawIter() {}
   1045         RawIter(const SkPath& path) {
   1046             setPath(path);
   1047         }
   1048 
   1049         void setPath(const SkPath& path) {
   1050             fRawIter.setPathRef(*path.fPathRef.get());
   1051         }
   1052 
   1053         /** Return the next verb in this iteration of the path. When all
   1054             segments have been visited, return kDone_Verb.
   1055 
   1056             @param  pts The points representing the current verb and/or segment
   1057                         This must not be NULL.
   1058             @return The verb for the current segment
   1059         */
   1060         Verb next(SkPoint pts[4]) {
   1061             return (Verb) fRawIter.next(pts);
   1062         }
   1063 
   1064         /** Return what the next verb will be, but do not visit the next segment.
   1065 
   1066             @return The verb for the next segment
   1067         */
   1068         Verb peek() const {
   1069             return (Verb) fRawIter.peek();
   1070         }
   1071 
   1072         SkScalar conicWeight() const {
   1073             return fRawIter.conicWeight();
   1074         }
   1075 
   1076     private:
   1077         SkPathRef::Iter fRawIter;
   1078         friend class SkPath;
   1079     };
   1080 
   1081     /**
   1082      *  Returns true if the point { x, y } is contained by the path, taking into
   1083      *  account the FillType.
   1084      */
   1085     bool contains(SkScalar x, SkScalar y) const;
   1086 
   1087     void dump(SkWStream* , bool forceClose, bool dumpAsHex) const;
   1088     void dump() const;
   1089     void dumpHex() const;
   1090 
   1091     /**
   1092      *  Write the path to the buffer, and return the number of bytes written.
   1093      *  If buffer is NULL, it still returns the number of bytes.
   1094      */
   1095     size_t writeToMemory(void* buffer) const;
   1096     /**
   1097      * Initializes the path from the buffer
   1098      *
   1099      * @param buffer Memory to read from
   1100      * @param length Amount of memory available in the buffer
   1101      * @return number of bytes read (must be a multiple of 4) or
   1102      *         0 if there was not enough memory available
   1103      */
   1104     size_t readFromMemory(const void* buffer, size_t length);
   1105 
   1106     /** Returns a non-zero, globally unique value corresponding to the set of verbs
   1107         and points in the path (but not the fill type [except on Android skbug.com/1762]).
   1108         Each time the path is modified, a different generation ID will be returned.
   1109     */
   1110     uint32_t getGenerationID() const;
   1111 
   1112 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
   1113     static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762)
   1114 #else
   1115     static const int kPathRefGenIDBitCnt = 32;
   1116 #endif
   1117 
   1118     SkDEBUGCODE(void validate() const;)
   1119     SkDEBUGCODE(void experimentalValidateRef() const { fPathRef->validate(); } )
   1120 
   1121 private:
   1122     enum SerializationOffsets {
   1123         // 1 free bit at 29
   1124         kUnused1_SerializationShift = 28,    // 1 free bit
   1125         kDirection_SerializationShift = 26, // requires 2 bits
   1126         kIsVolatile_SerializationShift = 25, // requires 1 bit
   1127         // 1 free bit at 24
   1128         kConvexity_SerializationShift = 16, // requires 8 bits
   1129         kFillType_SerializationShift = 8,   // requires 8 bits
   1130         // low-8-bits are version
   1131     };
   1132 
   1133     enum SerializationVersions {
   1134         kPathPrivFirstDirection_Version = 1,
   1135         kPathPrivLastMoveToIndex_Version = 2,
   1136         kCurrent_Version = 2
   1137     };
   1138 
   1139     sk_sp<SkPathRef>                                   fPathRef;
   1140     int                                                fLastMoveToIndex;
   1141     uint8_t                                            fFillType;
   1142     mutable uint8_t                                    fConvexity;
   1143     mutable SkAtomic<uint8_t, sk_memory_order_relaxed> fFirstDirection;// SkPathPriv::FirstDirection
   1144     SkBool8                                            fIsVolatile;
   1145 
   1146     /** Resets all fields other than fPathRef to their initial 'empty' values.
   1147      *  Assumes the caller has already emptied fPathRef.
   1148      *  On Android increments fGenerationID without reseting it.
   1149      */
   1150     void resetFields();
   1151 
   1152     /** Sets all fields other than fPathRef to the values in 'that'.
   1153      *  Assumes the caller has already set fPathRef.
   1154      *  Doesn't change fGenerationID or fSourcePath on Android.
   1155      */
   1156     void copyFields(const SkPath& that);
   1157 
   1158     friend class Iter;
   1159     friend class SkPathPriv;
   1160     friend class SkPathStroker;
   1161 
   1162     /*  Append, in reverse order, the first contour of path, ignoring path's
   1163         last point. If no moveTo() call has been made for this contour, the
   1164         first point is automatically set to (0,0).
   1165     */
   1166     void reversePathTo(const SkPath&);
   1167 
   1168     // called before we add points for lineTo, quadTo, cubicTo, checking to see
   1169     // if we need to inject a leading moveTo first
   1170     //
   1171     //  SkPath path; path.lineTo(...);   <--- need a leading moveTo(0, 0)
   1172     // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
   1173     //
   1174     inline void injectMoveToIfNeeded();
   1175 
   1176     inline bool hasOnlyMoveTos() const;
   1177 
   1178     Convexity internalGetConvexity() const;
   1179 
   1180     bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts,
   1181                        bool* isClosed, Direction* direction) const;
   1182 
   1183     // called by stroker to see if all points are equal and worthy of a cap
   1184     // equivalent to a short-circuit version of getBounds().isEmpty()
   1185     bool isZeroLength() const;
   1186 
   1187     /** Returns if the path can return a bound at no cost (true) or will have to
   1188         perform some computation (false).
   1189      */
   1190     bool hasComputedBounds() const {
   1191         SkDEBUGCODE(this->validate();)
   1192         return fPathRef->hasComputedBounds();
   1193     }
   1194 
   1195 
   1196     // 'rect' needs to be sorted
   1197     void setBounds(const SkRect& rect) {
   1198         SkPathRef::Editor ed(&fPathRef);
   1199 
   1200         ed.setBounds(rect);
   1201     }
   1202 
   1203     void setPt(int index, SkScalar x, SkScalar y);
   1204 
   1205     friend class SkAutoPathBoundsUpdate;
   1206     friend class SkAutoDisableOvalCheck;
   1207     friend class SkAutoDisableDirectionCheck;
   1208     friend class SkPathWriter;
   1209     friend class SkOpBuilder;
   1210     friend class SkBench_AddPathTest; // perf test reversePathTo
   1211     friend class PathTest_Private; // unit test reversePathTo
   1212     friend class ForceIsRRect_Private; // unit test isRRect
   1213 };
   1214 
   1215 #endif
   1216