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