Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef SkPath_DEFINED
     18 #define SkPath_DEFINED
     19 
     20 #include "SkMatrix.h"
     21 #include "SkTDArray.h"
     22 
     23 class SkFlattenableReadBuffer;
     24 class SkFlattenableWriteBuffer;
     25 class SkAutoPathBoundsUpdate;
     26 class SkString;
     27 
     28 /** \class SkPath
     29 
     30     The SkPath class encapsulates compound (multiple contour) geometric paths
     31     consisting of straight line segments, quadratic curves, and cubic curves.
     32 */
     33 class SkPath {
     34 public:
     35     SkPath();
     36     SkPath(const SkPath&);
     37     ~SkPath();
     38 
     39     SkPath& operator=(const SkPath&);
     40 
     41     friend bool operator==(const SkPath&, const SkPath&);
     42     friend bool operator!=(const SkPath& a, const SkPath& b) {
     43         return !(a == b);
     44     }
     45 
     46     enum FillType {
     47         /** Specifies that "inside" is computed by a non-zero sum of signed
     48             edge crossings
     49         */
     50         kWinding_FillType,
     51         /** Specifies that "inside" is computed by an odd number of edge
     52             crossings
     53         */
     54         kEvenOdd_FillType,
     55         /** Same as Winding, but draws outside of the path, rather than inside
     56         */
     57         kInverseWinding_FillType,
     58         /** Same as EvenOdd, but draws outside of the path, rather than inside
     59          */
     60         kInverseEvenOdd_FillType
     61     };
     62 
     63     /** Return the path's fill type. This is used to define how "inside" is
     64         computed. The default value is kWinding_FillType.
     65 
     66         @return the path's fill type
     67     */
     68     FillType getFillType() const { return (FillType)fFillType; }
     69 
     70     /** Set the path's fill type. This is used to define how "inside" is
     71         computed. The default value is kWinding_FillType.
     72 
     73         @param ft The new fill type for this path
     74     */
     75     void setFillType(FillType ft) { fFillType = SkToU8(ft); }
     76 
     77     /** Returns true if the filltype is one of the Inverse variants */
     78     bool isInverseFillType() const { return (fFillType & 2) != 0; }
     79 
     80     /** Toggle between inverse and normal filltypes. This reverse the return
     81         value of isInverseFillType()
     82     */
     83     void toggleInverseFillType() { fFillType ^= 2; }
     84 
     85     /** Returns true if the path is flagged as being convex. This is not a
     86         confirmed by any analysis, it is just the value set earlier.
     87      */
     88     bool isConvex() const { return fIsConvex != 0; }
     89 
     90     /** Set the isConvex flag to true or false. Convex paths may draw faster if
     91         this flag is set, though setting this to true on a path that is in fact
     92         not convex can give undefined results when drawn. Paths default to
     93         isConvex == false
     94      */
     95     void setIsConvex(bool isConvex) { fIsConvex = (isConvex != 0); }
     96 
     97     /** Clear any lines and curves from the path, making it empty. This frees up
     98         internal storage associated with those segments.
     99         This does NOT change the fill-type setting nor isConvex
    100     */
    101     void reset();
    102 
    103     /** Similar to reset(), in that all lines and curves are removed from the
    104         path. However, any internal storage for those lines/curves is retained,
    105         making reuse of the path potentially faster.
    106         This does NOT change the fill-type setting nor isConvex
    107     */
    108     void rewind();
    109 
    110     /** Returns true if the path is empty (contains no lines or curves)
    111 
    112         @return true if the path is empty (contains no lines or curves)
    113     */
    114     bool isEmpty() const;
    115 
    116     /** Returns true if the path specifies a rectangle. If so, and if rect is
    117         not null, set rect to the bounds of the path. If the path does not
    118         specify a rectangle, return false and ignore rect.
    119 
    120         @param rect If not null, returns the bounds of the path if it specifies
    121                     a rectangle
    122         @return true if the path specifies a rectangle
    123     */
    124     bool isRect(SkRect* rect) const;
    125 
    126     /** Return the number of points in the path
    127      */
    128     int countPoints() const {
    129         return this->getPoints(NULL, 0);
    130     }
    131 
    132     /** Return the point at the specified index. If the index is out of range
    133          (i.e. is not 0 <= index < countPoints()) then the returned coordinates
    134          will be (0,0)
    135      */
    136     SkPoint getPoint(int index) const;
    137 
    138     /** Returns the number of points in the path. Up to max points are copied.
    139 
    140         @param points If not null, receives up to max points
    141         @param max The maximum number of points to copy into points
    142         @return the actual number of points in the path
    143     */
    144     int getPoints(SkPoint points[], int max) const;
    145 
    146     //! Swap contents of this and other. Guaranteed not to throw
    147     void swap(SkPath& other);
    148 
    149     /** Returns the bounds of the path's points. If the path contains 0 or 1
    150         points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
    151         Note: this bounds may be larger than the actual shape, since curves
    152         do not extend as far as their control points.
    153     */
    154     const SkRect& getBounds() const {
    155         if (fBoundsIsDirty) {
    156             this->computeBounds();
    157         }
    158         return fBounds;
    159     }
    160 
    161     /** Calling this will, if the internal cache of the bounds is out of date,
    162         update it so that subsequent calls to getBounds will be instanteous.
    163         This also means that any copies or simple transformations of the path
    164         will inherit the cached bounds.
    165      */
    166     void updateBoundsCache() const {
    167         // for now, just calling getBounds() is sufficient
    168         this->getBounds();
    169     }
    170 
    171     //  Construction methods
    172 
    173     /** Hint to the path to prepare for adding more points. This can allow the
    174         path to more efficiently grow its storage.
    175 
    176         @param extraPtCount The number of extra points the path should
    177                             preallocate for.
    178     */
    179     void incReserve(unsigned extraPtCount);
    180 
    181     /** Set the beginning of the next contour to the point (x,y).
    182 
    183         @param x    The x-coordinate of the start of a new contour
    184         @param y    The y-coordinate of the start of a new contour
    185     */
    186     void moveTo(SkScalar x, SkScalar y);
    187 
    188     /** Set the beginning of the next contour to the point
    189 
    190         @param p    The start of a new contour
    191     */
    192     void moveTo(const SkPoint& p) {
    193         this->moveTo(p.fX, p.fY);
    194     }
    195 
    196     /** Set the beginning of the next contour relative to the last point on the
    197         previous contour. If there is no previous contour, this is treated the
    198         same as moveTo().
    199 
    200         @param dx   The amount to add to the x-coordinate of the end of the
    201                     previous contour, to specify the start of a new contour
    202         @param dy   The amount to add to the y-coordinate of the end of the
    203                     previous contour, to specify the start of a new contour
    204     */
    205     void rMoveTo(SkScalar dx, SkScalar dy);
    206 
    207     /** Add a line from the last point to the specified point (x,y). If no
    208         moveTo() call has been made for this contour, the first point is
    209         automatically set to (0,0).
    210 
    211         @param x    The x-coordinate of the end of a line
    212         @param y    The y-coordinate of the end of a line
    213     */
    214     void lineTo(SkScalar x, SkScalar y);
    215 
    216     /** Add a line from the last point to the specified point. If no moveTo()
    217         call has been made for this contour, the first point is automatically
    218         set to (0,0).
    219 
    220         @param p    The end of a line
    221     */
    222     void lineTo(const SkPoint& p) {
    223         this->lineTo(p.fX, p.fY);
    224     }
    225 
    226     /** Same as lineTo, but the coordinates are considered relative to the last
    227         point on this contour. If there is no previous point, then a moveTo(0,0)
    228         is inserted automatically.
    229 
    230         @param dx   The amount to add to the x-coordinate of the previous point
    231                     on this contour, to specify a line
    232         @param dy   The amount to add to the y-coordinate of the previous point
    233                     on this contour, to specify a line
    234     */
    235     void rLineTo(SkScalar dx, SkScalar dy);
    236 
    237     /** Add a quadratic bezier from the last point, approaching control point
    238         (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
    239         this contour, the first point is automatically set to (0,0).
    240 
    241         @param x1   The x-coordinate of the control point on a quadratic curve
    242         @param y1   The y-coordinate of the control point on a quadratic curve
    243         @param x2   The x-coordinate of the end point on a quadratic curve
    244         @param y2   The y-coordinate of the end point on a quadratic curve
    245     */
    246     void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2);
    247 
    248     /** Add a quadratic bezier from the last point, approaching control point
    249         p1, and ending at p2. If no moveTo() call has been made for this
    250         contour, the first point is automatically set to (0,0).
    251 
    252         @param p1   The control point on a quadratic curve
    253         @param p2   The end point on a quadratic curve
    254     */
    255     void quadTo(const SkPoint& p1, const SkPoint& p2) {
    256         this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY);
    257     }
    258 
    259     /** Same as quadTo, but the coordinates are considered relative to the last
    260         point on this contour. If there is no previous point, then a moveTo(0,0)
    261         is inserted automatically.
    262 
    263         @param dx1   The amount to add to the x-coordinate of the last point on
    264                 this contour, to specify the control point of a quadratic curve
    265         @param dy1   The amount to add to the y-coordinate of the last point on
    266                 this contour, to specify the control point of a quadratic curve
    267         @param dx2   The amount to add to the x-coordinate of the last point on
    268                      this contour, to specify the end point of a quadratic curve
    269         @param dy2   The amount to add to the y-coordinate of the last point on
    270                      this contour, to specify the end point of a quadratic curve
    271     */
    272     void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
    273 
    274     /** Add a cubic bezier from the last point, approaching control points
    275         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
    276         made for this contour, the first point is automatically set to (0,0).
    277 
    278         @param x1   The x-coordinate of the 1st control point on a cubic curve
    279         @param y1   The y-coordinate of the 1st control point on a cubic curve
    280         @param x2   The x-coordinate of the 2nd control point on a cubic curve
    281         @param y2   The y-coordinate of the 2nd control point on a cubic curve
    282         @param x3   The x-coordinate of the end point on a cubic curve
    283         @param y3   The y-coordinate of the end point on a cubic curve
    284     */
    285     void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    286                  SkScalar x3, SkScalar y3);
    287 
    288     /** Add a cubic bezier from the last point, approaching control points p1
    289         and p2, and ending at p3. If no moveTo() call has been made for this
    290         contour, the first point is automatically set to (0,0).
    291 
    292         @param p1   The 1st control point on a cubic curve
    293         @param p2   The 2nd control point on a cubic curve
    294         @param p3   The end point on a cubic curve
    295     */
    296     void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) {
    297         this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY);
    298     }
    299 
    300     /** Same as cubicTo, but the coordinates are considered relative to the
    301         current point on this contour. If there is no previous point, then a
    302         moveTo(0,0) is inserted automatically.
    303 
    304         @param dx1   The amount to add to the x-coordinate of the last point on
    305                 this contour, to specify the 1st control point of a cubic curve
    306         @param dy1   The amount to add to the y-coordinate of the last point on
    307                 this contour, to specify the 1st control point of a cubic curve
    308         @param dx2   The amount to add to the x-coordinate of the last point on
    309                 this contour, to specify the 2nd control point of a cubic curve
    310         @param dy2   The amount to add to the y-coordinate of the last point on
    311                 this contour, to specify the 2nd control point of a cubic curve
    312         @param dx3   The amount to add to the x-coordinate of the last point on
    313                      this contour, to specify the end point of a cubic curve
    314         @param dy3   The amount to add to the y-coordinate of the last point on
    315                      this contour, to specify the end point of a cubic curve
    316     */
    317     void    rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    318                      SkScalar x3, SkScalar y3);
    319 
    320     /** Append the specified arc to the path as a new contour. If the start of
    321         the path is different from the path's current last point, then an
    322         automatic lineTo() is added to connect the current contour to the start
    323         of the arc. However, if the path is empty, then we call moveTo() with
    324         the first point of the arc. The sweep angle is treated mod 360.
    325 
    326         @param oval The bounding oval defining the shape and size of the arc
    327         @param startAngle Starting angle (in degrees) where the arc begins
    328         @param sweepAngle Sweep angle (in degrees) measured clockwise. This is
    329                           treated mod 360.
    330         @param forceMoveTo If true, always begin a new contour with the arc
    331     */
    332     void    arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
    333                   bool forceMoveTo);
    334 
    335     /** Append a line and arc to the current path. This is the same as the
    336         PostScript call "arct".
    337     */
    338     void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
    339                SkScalar radius);
    340 
    341     /** Append a line and arc to the current path. This is the same as the
    342         PostScript call "arct".
    343     */
    344     void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) {
    345         this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius);
    346     }
    347 
    348     /** Close the current contour. If the current point is not equal to the
    349         first point of the contour, a line segment is automatically added.
    350     */
    351     void close();
    352 
    353     enum Direction {
    354         /** clockwise direction for adding closed contours */
    355         kCW_Direction,
    356         /** counter-clockwise direction for adding closed contours */
    357         kCCW_Direction
    358     };
    359 
    360     /** Add a closed rectangle contour to the path
    361         @param rect The rectangle to add as a closed contour to the path
    362         @param dir  The direction to wind the rectangle's contour
    363     */
    364     void    addRect(const SkRect& rect, Direction dir = kCW_Direction);
    365 
    366     /** Add a closed rectangle contour to the path
    367 
    368         @param left     The left side of a rectangle to add as a closed contour
    369                         to the path
    370         @param top      The top of a rectangle to add as a closed contour to the
    371                         path
    372         @param right    The right side of a rectangle to add as a closed contour
    373                         to the path
    374         @param bottom   The bottom of a rectangle to add as a closed contour to
    375                         the path
    376         @param dir      The direction to wind the rectangle's contour
    377     */
    378     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
    379                  Direction dir = kCW_Direction);
    380 
    381     /** Add a closed oval contour to the path
    382 
    383         @param oval The bounding oval to add as a closed contour to the path
    384         @param dir  The direction to wind the oval's contour
    385     */
    386     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
    387 
    388     /** Add a closed circle contour to the path
    389 
    390         @param x        The x-coordinate of the center of a circle to add as a
    391                         closed contour to the path
    392         @param y        The y-coordinate of the center of a circle to add as a
    393                         closed contour to the path
    394         @param radius   The radius of a circle to add as a closed contour to the
    395                         path
    396         @param dir      The direction to wind the circle's contour
    397     */
    398     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
    399                    Direction dir = kCW_Direction);
    400 
    401     /** Add the specified arc to the path as a new contour.
    402 
    403         @param oval The bounds of oval used to define the size of the arc
    404         @param startAngle Starting angle (in degrees) where the arc begins
    405         @param sweepAngle Sweep angle (in degrees) measured clockwise
    406     */
    407     void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
    408 
    409     /** Add a closed round-rectangle contour to the path
    410         @param rect The bounds of a round-rectangle to add as a closed contour
    411         @param rx   The x-radius of the rounded corners on the round-rectangle
    412         @param ry   The y-radius of the rounded corners on the round-rectangle
    413         @param dir  The direction to wind the round-rectangle's contour
    414     */
    415     void    addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
    416                          Direction dir = kCW_Direction);
    417 
    418     /** Add a closed round-rectangle contour to the path. Each corner receives
    419         two radius values [X, Y]. The corners are ordered top-left, top-right,
    420         bottom-right, bottom-left.
    421         @param rect The bounds of a round-rectangle to add as a closed contour
    422         @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
    423         @param dir  The direction to wind the round-rectangle's contour
    424         */
    425     void addRoundRect(const SkRect& rect, const SkScalar radii[],
    426                       Direction dir = kCW_Direction);
    427 
    428     /** Add a copy of src to the path, offset by (dx,dy)
    429         @param src  The path to add as a new contour
    430         @param dx   The amount to translate the path in X as it is added
    431         @param dx   The amount to translate the path in Y as it is added
    432     */
    433     void    addPath(const SkPath& src, SkScalar dx, SkScalar dy);
    434 
    435     /** Add a copy of src to the path
    436     */
    437     void addPath(const SkPath& src) {
    438         SkMatrix m;
    439         m.reset();
    440         this->addPath(src, m);
    441     }
    442 
    443     /** Add a copy of src to the path, transformed by matrix
    444         @param src  The path to add as a new contour
    445     */
    446     void addPath(const SkPath& src, const SkMatrix& matrix);
    447 
    448     /** Offset the path by (dx,dy), returning true on success
    449 
    450         @param dx   The amount in the X direction to offset the entire path
    451         @param dy   The amount in the Y direction to offset the entire path
    452         @param dst  The translated path is written here
    453     */
    454     void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
    455 
    456     /** Offset the path by (dx,dy), returning true on success
    457 
    458         @param dx   The amount in the X direction to offset the entire path
    459         @param dy   The amount in the Y direction to offset the entire path
    460     */
    461     void offset(SkScalar dx, SkScalar dy) {
    462         this->offset(dx, dy, this);
    463     }
    464 
    465     /** Transform the points in this path by matrix, and write the answer into
    466         dst.
    467 
    468         @param matrix   The matrix to apply to the path
    469         @param dst      The transformed path is written here
    470     */
    471     void transform(const SkMatrix& matrix, SkPath* dst) const;
    472 
    473     /** Transform the points in this path by matrix
    474 
    475         @param matrix The matrix to apply to the path
    476     */
    477     void transform(const SkMatrix& matrix) {
    478         this->transform(matrix, this);
    479     }
    480 
    481     /** Return the last point on the path. If no points have been added, (0,0)
    482         is returned.
    483 
    484         @param lastPt   The last point on the path is returned here
    485     */
    486     void getLastPt(SkPoint* lastPt) const;
    487 
    488     /** Set the last point on the path. If no points have been added,
    489         moveTo(x,y) is automatically called.
    490 
    491         @param x    The new x-coordinate for the last point
    492         @param y    The new y-coordinate for the last point
    493     */
    494     void setLastPt(SkScalar x, SkScalar y);
    495 
    496     /** Set the last point on the path. If no points have been added, moveTo(p)
    497         is automatically called.
    498 
    499         @param p    The new location for the last point
    500     */
    501     void setLastPt(const SkPoint& p) {
    502         this->setLastPt(p.fX, p.fY);
    503     }
    504 
    505     enum Verb {
    506         kMove_Verb,     //!< iter.next returns 1 point
    507         kLine_Verb,     //!< iter.next returns 2 points
    508         kQuad_Verb,     //!< iter.next returns 3 points
    509         kCubic_Verb,    //!< iter.next returns 4 points
    510         kClose_Verb,    //!< iter.next returns 1 point (the last point)
    511         kDone_Verb      //!< iter.next returns 0 points
    512     };
    513 
    514     /** Iterate through all of the segments (lines, quadratics, cubics) of
    515         each contours in a path.
    516     */
    517     class Iter {
    518     public:
    519                 Iter();
    520                 Iter(const SkPath&, bool forceClose);
    521 
    522         void setPath(const SkPath&, bool forceClose);
    523 
    524         /** Return the next verb in this iteration of the path. When all
    525             segments have been visited, return kDone_Verb.
    526 
    527             @param  pts The points representing the current verb and/or segment
    528             @return The verb for the current segment
    529         */
    530         Verb next(SkPoint pts[4]);
    531 
    532         /** If next() returns kLine_Verb, then this query returns true if the
    533             line was the result of a close() command (i.e. the end point is the
    534             initial moveto for this contour). If next() returned a different
    535             verb, this returns an undefined value.
    536 
    537             @return If the last call to next() returned kLine_Verb, return true
    538                     if it was the result of an explicit close command.
    539         */
    540         bool isCloseLine() const { return SkToBool(fCloseLine); }
    541 
    542         /** Returns true if the current contour is closed (has a kClose_Verb)
    543             @return true if the current contour is closed (has a kClose_Verb)
    544         */
    545         bool isClosedContour() const;
    546 
    547     private:
    548         const SkPoint*  fPts;
    549         const uint8_t*  fVerbs;
    550         const uint8_t*  fVerbStop;
    551         SkPoint         fMoveTo;
    552         SkPoint         fLastPt;
    553         SkBool8         fForceClose;
    554         SkBool8         fNeedClose;
    555         SkBool8         fNeedMoveTo;
    556         SkBool8         fCloseLine;
    557 
    558         bool cons_moveTo(SkPoint pts[1]);
    559         Verb autoClose(SkPoint pts[2]);
    560     };
    561 
    562     void dump(bool forceClose, const char title[] = NULL) const;
    563     void dump() const;
    564 
    565     void flatten(SkFlattenableWriteBuffer&) const;
    566     void unflatten(SkFlattenableReadBuffer&);
    567 
    568     /** Subdivide the path so that no segment is longer that dist.
    569         If bendLines is true, then turn all line segments into curves.
    570         If dst == null, then the original path itself is modified (not const!)
    571     */
    572     void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const;
    573 
    574     SkDEBUGCODE(void validate() const;)
    575 
    576 private:
    577     SkTDArray<SkPoint>  fPts;
    578     SkTDArray<uint8_t>  fVerbs;
    579     mutable SkRect      fBounds;
    580     mutable uint8_t     fBoundsIsDirty;
    581     uint8_t             fFillType;
    582     uint8_t             fIsConvex;
    583 
    584     // called, if dirty, by getBounds()
    585     void computeBounds() const;
    586 
    587     friend class Iter;
    588     void cons_moveto();
    589 
    590     friend class SkPathStroker;
    591     /*  Append the first contour of path, ignoring path's initial point. If no
    592         moveTo() call has been made for this contour, the first point is
    593         automatically set to (0,0).
    594     */
    595     void pathTo(const SkPath& path);
    596 
    597     /*  Append, in reverse order, the first contour of path, ignoring path's
    598         last point. If no moveTo() call has been made for this contour, the
    599         first point is automatically set to (0,0).
    600     */
    601     void reversePathTo(const SkPath&);
    602 
    603     friend const SkPoint* sk_get_path_points(const SkPath&, int index);
    604     friend class SkAutoPathBoundsUpdate;
    605 };
    606 
    607 #endif
    608 
    609