michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #ifndef SkPath_DEFINED michael@0: #define SkPath_DEFINED michael@0: michael@0: #include "SkInstCnt.h" michael@0: #include "SkMatrix.h" michael@0: #include "SkPathRef.h" michael@0: #include "SkTDArray.h" michael@0: #include "SkRefCnt.h" michael@0: michael@0: class SkReader32; michael@0: class SkWriter32; michael@0: class SkAutoPathBoundsUpdate; michael@0: class SkString; michael@0: class SkRRect; michael@0: michael@0: /** \class SkPath michael@0: michael@0: The SkPath class encapsulates compound (multiple contour) geometric paths michael@0: consisting of straight line segments, quadratic curves, and cubic curves. michael@0: */ michael@0: class SK_API SkPath { michael@0: public: michael@0: SK_DECLARE_INST_COUNT_ROOT(SkPath); michael@0: michael@0: SkPath(); michael@0: SkPath(const SkPath&); michael@0: ~SkPath(); michael@0: michael@0: SkPath& operator=(const SkPath&); michael@0: friend SK_API bool operator==(const SkPath&, const SkPath&); michael@0: friend bool operator!=(const SkPath& a, const SkPath& b) { michael@0: return !(a == b); michael@0: } michael@0: michael@0: enum FillType { michael@0: /** Specifies that "inside" is computed by a non-zero sum of signed michael@0: edge crossings michael@0: */ michael@0: kWinding_FillType, michael@0: /** Specifies that "inside" is computed by an odd number of edge michael@0: crossings michael@0: */ michael@0: kEvenOdd_FillType, michael@0: /** Same as Winding, but draws outside of the path, rather than inside michael@0: */ michael@0: kInverseWinding_FillType, michael@0: /** Same as EvenOdd, but draws outside of the path, rather than inside michael@0: */ michael@0: kInverseEvenOdd_FillType michael@0: }; michael@0: michael@0: /** Return the path's fill type. This is used to define how "inside" is michael@0: computed. The default value is kWinding_FillType. michael@0: michael@0: @return the path's fill type michael@0: */ michael@0: FillType getFillType() const { return (FillType)fFillType; } michael@0: michael@0: /** Set the path's fill type. This is used to define how "inside" is michael@0: computed. The default value is kWinding_FillType. michael@0: michael@0: @param ft The new fill type for this path michael@0: */ michael@0: void setFillType(FillType ft) { michael@0: fFillType = SkToU8(ft); michael@0: } michael@0: michael@0: /** Returns true if the filltype is one of the Inverse variants */ michael@0: bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); } michael@0: michael@0: /** michael@0: * Toggle between inverse and normal filltypes. This reverse the return michael@0: * value of isInverseFillType() michael@0: */ michael@0: void toggleInverseFillType() { michael@0: fFillType ^= 2; michael@0: } michael@0: michael@0: enum Convexity { michael@0: kUnknown_Convexity, michael@0: kConvex_Convexity, michael@0: kConcave_Convexity michael@0: }; michael@0: michael@0: /** michael@0: * Return the path's convexity, as stored in the path. If it is currently unknown, michael@0: * then this function will attempt to compute the convexity (and cache the result). michael@0: */ michael@0: Convexity getConvexity() const { michael@0: if (kUnknown_Convexity != fConvexity) { michael@0: return static_cast(fConvexity); michael@0: } else { michael@0: return this->internalGetConvexity(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Return the currently cached value for convexity, even if that is set to michael@0: * kUnknown_Convexity. Note: getConvexity() will automatically call michael@0: * ComputeConvexity and cache its return value if the current setting is michael@0: * kUnknown. michael@0: */ michael@0: Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; } michael@0: michael@0: /** michael@0: * Store a convexity setting in the path. There is no automatic check to michael@0: * see if this value actually agrees with the return value that would be michael@0: * computed by getConvexity(). michael@0: * michael@0: * Note: even if this is set to a "known" value, if the path is later michael@0: * changed (e.g. lineTo(), addRect(), etc.) then the cached value will be michael@0: * reset to kUnknown_Convexity. michael@0: */ michael@0: void setConvexity(Convexity); michael@0: michael@0: /** michael@0: * Returns true if the path is flagged as being convex. This is not a michael@0: * confirmed by any analysis, it is just the value set earlier. michael@0: */ michael@0: bool isConvex() const { michael@0: return kConvex_Convexity == this->getConvexity(); michael@0: } michael@0: michael@0: /** michael@0: * Set the isConvex flag to true or false. Convex paths may draw faster if michael@0: * this flag is set, though setting this to true on a path that is in fact michael@0: * not convex can give undefined results when drawn. Paths default to michael@0: * isConvex == false michael@0: */ michael@0: SK_ATTR_DEPRECATED("use setConvexity") michael@0: void setIsConvex(bool isConvex) { michael@0: this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity); michael@0: } michael@0: michael@0: /** Returns true if the path is an oval. michael@0: * michael@0: * @param rect returns the bounding rect of this oval. It's a circle michael@0: * if the height and width are the same. michael@0: * michael@0: * @return true if this path is an oval. michael@0: * Tracking whether a path is an oval is considered an michael@0: * optimization for performance and so some paths that are in michael@0: * fact ovals can report false. michael@0: */ michael@0: bool isOval(SkRect* rect) const { return fPathRef->isOval(rect); } michael@0: michael@0: /** Clear any lines and curves from the path, making it empty. This frees up michael@0: internal storage associated with those segments. michael@0: On Android, does not change fSourcePath. michael@0: */ michael@0: void reset(); michael@0: michael@0: /** Similar to reset(), in that all lines and curves are removed from the michael@0: path. However, any internal storage for those lines/curves is retained, michael@0: making reuse of the path potentially faster. michael@0: On Android, does not change fSourcePath. michael@0: */ michael@0: void rewind(); michael@0: michael@0: /** Returns true if the path is empty (contains no lines or curves) michael@0: michael@0: @return true if the path is empty (contains no lines or curves) michael@0: */ michael@0: bool isEmpty() const { michael@0: SkDEBUGCODE(this->validate();) michael@0: return 0 == fPathRef->countVerbs(); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if all of the points in this path are finite, meaning there michael@0: * are no infinities and no NaNs. michael@0: */ michael@0: bool isFinite() const { michael@0: SkDEBUGCODE(this->validate();) michael@0: return fPathRef->isFinite(); michael@0: } michael@0: michael@0: /** Test a line for zero length michael@0: michael@0: @return true if the line is of zero length; otherwise false. michael@0: */ michael@0: static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2) { michael@0: return p1.equalsWithinTolerance(p2); michael@0: } michael@0: michael@0: /** Test a quad for zero length michael@0: michael@0: @return true if the quad is of zero length; otherwise false. michael@0: */ michael@0: static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, michael@0: const SkPoint& p3) { michael@0: return p1.equalsWithinTolerance(p2) && michael@0: p2.equalsWithinTolerance(p3); michael@0: } michael@0: michael@0: /** Test a cubic curve for zero length michael@0: michael@0: @return true if the cubic is of zero length; otherwise false. michael@0: */ michael@0: static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, michael@0: const SkPoint& p3, const SkPoint& p4) { michael@0: return p1.equalsWithinTolerance(p2) && michael@0: p2.equalsWithinTolerance(p3) && michael@0: p3.equalsWithinTolerance(p4); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the path specifies a single line (i.e. it contains just michael@0: * a moveTo and a lineTo). If so, and line[] is not null, it sets the 2 michael@0: * points in line[] to the end-points of the line. If the path is not a michael@0: * line, returns false and ignores line[]. michael@0: */ michael@0: bool isLine(SkPoint line[2]) const; michael@0: michael@0: /** Returns true if the path specifies a rectangle. If so, and if rect is michael@0: not null, set rect to the bounds of the path. If the path does not michael@0: specify a rectangle, return false and ignore rect. michael@0: michael@0: @param rect If not null, returns the bounds of the path if it specifies michael@0: a rectangle michael@0: @return true if the path specifies a rectangle michael@0: */ michael@0: bool isRect(SkRect* rect) const; michael@0: michael@0: /** Return the number of points in the path michael@0: */ michael@0: int countPoints() const; michael@0: michael@0: /** Return the point at the specified index. If the index is out of range michael@0: (i.e. is not 0 <= index < countPoints()) then the returned coordinates michael@0: will be (0,0) michael@0: */ michael@0: SkPoint getPoint(int index) const; michael@0: michael@0: /** Returns the number of points in the path. Up to max points are copied. michael@0: michael@0: @param points If not null, receives up to max points michael@0: @param max The maximum number of points to copy into points michael@0: @return the actual number of points in the path michael@0: */ michael@0: int getPoints(SkPoint points[], int max) const; michael@0: michael@0: /** Return the number of verbs in the path michael@0: */ michael@0: int countVerbs() const; michael@0: michael@0: /** Returns the number of verbs in the path. Up to max verbs are copied. The michael@0: verbs are copied as one byte per verb. michael@0: michael@0: @param verbs If not null, receives up to max verbs michael@0: @param max The maximum number of verbs to copy into verbs michael@0: @return the actual number of verbs in the path michael@0: */ michael@0: int getVerbs(uint8_t verbs[], int max) const; michael@0: michael@0: //! Swap contents of this and other. Guaranteed not to throw michael@0: void swap(SkPath& other); michael@0: michael@0: /** Returns the bounds of the path's points. If the path contains 0 or 1 michael@0: points, the bounds is set to (0,0,0,0), and isEmpty() will return true. michael@0: Note: this bounds may be larger than the actual shape, since curves michael@0: do not extend as far as their control points. michael@0: */ michael@0: const SkRect& getBounds() const { michael@0: return fPathRef->getBounds(); michael@0: } michael@0: michael@0: /** Calling this will, if the internal cache of the bounds is out of date, michael@0: update it so that subsequent calls to getBounds will be instantaneous. michael@0: This also means that any copies or simple transformations of the path michael@0: will inherit the cached bounds. michael@0: */ michael@0: void updateBoundsCache() const { michael@0: // for now, just calling getBounds() is sufficient michael@0: this->getBounds(); michael@0: } michael@0: michael@0: /** michael@0: * Does a conservative test to see whether a rectangle is inside a path. Currently it only michael@0: * will ever return true for single convex contour paths. The empty-status of the rect is not michael@0: * considered (e.g. a rect that is a point can be inside a path). Points or line segments where michael@0: * the rect edge touches the path border are not considered containment violations. michael@0: */ michael@0: bool conservativelyContainsRect(const SkRect& rect) const; michael@0: michael@0: // Construction methods michael@0: michael@0: /** Hint to the path to prepare for adding more points. This can allow the michael@0: path to more efficiently grow its storage. michael@0: michael@0: @param extraPtCount The number of extra points the path should michael@0: preallocate for. michael@0: */ michael@0: void incReserve(unsigned extraPtCount); michael@0: michael@0: /** Set the beginning of the next contour to the point (x,y). michael@0: michael@0: @param x The x-coordinate of the start of a new contour michael@0: @param y The y-coordinate of the start of a new contour michael@0: */ michael@0: void moveTo(SkScalar x, SkScalar y); michael@0: michael@0: /** Set the beginning of the next contour to the point michael@0: michael@0: @param p The start of a new contour michael@0: */ michael@0: void moveTo(const SkPoint& p) { michael@0: this->moveTo(p.fX, p.fY); michael@0: } michael@0: michael@0: /** Set the beginning of the next contour relative to the last point on the michael@0: previous contour. If there is no previous contour, this is treated the michael@0: same as moveTo(). michael@0: michael@0: @param dx The amount to add to the x-coordinate of the end of the michael@0: previous contour, to specify the start of a new contour michael@0: @param dy The amount to add to the y-coordinate of the end of the michael@0: previous contour, to specify the start of a new contour michael@0: */ michael@0: void rMoveTo(SkScalar dx, SkScalar dy); michael@0: michael@0: /** Add a line from the last point to the specified point (x,y). If no michael@0: moveTo() call has been made for this contour, the first point is michael@0: automatically set to (0,0). michael@0: michael@0: @param x The x-coordinate of the end of a line michael@0: @param y The y-coordinate of the end of a line michael@0: */ michael@0: void lineTo(SkScalar x, SkScalar y); michael@0: michael@0: /** Add a line from the last point to the specified point. If no moveTo() michael@0: call has been made for this contour, the first point is automatically michael@0: set to (0,0). michael@0: michael@0: @param p The end of a line michael@0: */ michael@0: void lineTo(const SkPoint& p) { michael@0: this->lineTo(p.fX, p.fY); michael@0: } michael@0: michael@0: /** Same as lineTo, but the coordinates are considered relative to the last michael@0: point on this contour. If there is no previous point, then a moveTo(0,0) michael@0: is inserted automatically. michael@0: michael@0: @param dx The amount to add to the x-coordinate of the previous point michael@0: on this contour, to specify a line michael@0: @param dy The amount to add to the y-coordinate of the previous point michael@0: on this contour, to specify a line michael@0: */ michael@0: void rLineTo(SkScalar dx, SkScalar dy); michael@0: michael@0: /** Add a quadratic bezier from the last point, approaching control point michael@0: (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for michael@0: this contour, the first point is automatically set to (0,0). michael@0: michael@0: @param x1 The x-coordinate of the control point on a quadratic curve michael@0: @param y1 The y-coordinate of the control point on a quadratic curve michael@0: @param x2 The x-coordinate of the end point on a quadratic curve michael@0: @param y2 The y-coordinate of the end point on a quadratic curve michael@0: */ michael@0: void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); michael@0: michael@0: /** Add a quadratic bezier from the last point, approaching control point michael@0: p1, and ending at p2. If no moveTo() call has been made for this michael@0: contour, the first point is automatically set to (0,0). michael@0: michael@0: @param p1 The control point on a quadratic curve michael@0: @param p2 The end point on a quadratic curve michael@0: */ michael@0: void quadTo(const SkPoint& p1, const SkPoint& p2) { michael@0: this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); michael@0: } michael@0: michael@0: /** Same as quadTo, but the coordinates are considered relative to the last michael@0: point on this contour. If there is no previous point, then a moveTo(0,0) michael@0: is inserted automatically. michael@0: michael@0: @param dx1 The amount to add to the x-coordinate of the last point on michael@0: this contour, to specify the control point of a quadratic curve michael@0: @param dy1 The amount to add to the y-coordinate of the last point on michael@0: this contour, to specify the control point of a quadratic curve michael@0: @param dx2 The amount to add to the x-coordinate of the last point on michael@0: this contour, to specify the end point of a quadratic curve michael@0: @param dy2 The amount to add to the y-coordinate of the last point on michael@0: this contour, to specify the end point of a quadratic curve michael@0: */ michael@0: void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); michael@0: michael@0: void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, michael@0: SkScalar w); michael@0: void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) { michael@0: this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w); michael@0: } michael@0: void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, michael@0: SkScalar w); michael@0: michael@0: /** Add a cubic bezier from the last point, approaching control points michael@0: (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been michael@0: made for this contour, the first point is automatically set to (0,0). michael@0: michael@0: @param x1 The x-coordinate of the 1st control point on a cubic curve michael@0: @param y1 The y-coordinate of the 1st control point on a cubic curve michael@0: @param x2 The x-coordinate of the 2nd control point on a cubic curve michael@0: @param y2 The y-coordinate of the 2nd control point on a cubic curve michael@0: @param x3 The x-coordinate of the end point on a cubic curve michael@0: @param y3 The y-coordinate of the end point on a cubic curve michael@0: */ michael@0: void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, michael@0: SkScalar x3, SkScalar y3); michael@0: michael@0: /** Add a cubic bezier from the last point, approaching control points p1 michael@0: and p2, and ending at p3. If no moveTo() call has been made for this michael@0: contour, the first point is automatically set to (0,0). michael@0: michael@0: @param p1 The 1st control point on a cubic curve michael@0: @param p2 The 2nd control point on a cubic curve michael@0: @param p3 The end point on a cubic curve michael@0: */ michael@0: void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) { michael@0: this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); michael@0: } michael@0: michael@0: /** Same as cubicTo, but the coordinates are considered relative to the michael@0: current point on this contour. If there is no previous point, then a michael@0: moveTo(0,0) is inserted automatically. michael@0: michael@0: @param dx1 The amount to add to the x-coordinate of the last point on michael@0: this contour, to specify the 1st control point of a cubic curve michael@0: @param dy1 The amount to add to the y-coordinate of the last point on michael@0: this contour, to specify the 1st control point of a cubic curve michael@0: @param dx2 The amount to add to the x-coordinate of the last point on michael@0: this contour, to specify the 2nd control point of a cubic curve michael@0: @param dy2 The amount to add to the y-coordinate of the last point on michael@0: this contour, to specify the 2nd control point of a cubic curve michael@0: @param dx3 The amount to add to the x-coordinate of the last point on michael@0: this contour, to specify the end point of a cubic curve michael@0: @param dy3 The amount to add to the y-coordinate of the last point on michael@0: this contour, to specify the end point of a cubic curve michael@0: */ michael@0: void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, michael@0: SkScalar x3, SkScalar y3); michael@0: michael@0: /** Append the specified arc to the path as a new contour. If the start of michael@0: the path is different from the path's current last point, then an michael@0: automatic lineTo() is added to connect the current contour to the start michael@0: of the arc. However, if the path is empty, then we call moveTo() with michael@0: the first point of the arc. The sweep angle is treated mod 360. michael@0: michael@0: @param oval The bounding oval defining the shape and size of the arc michael@0: @param startAngle Starting angle (in degrees) where the arc begins michael@0: @param sweepAngle Sweep angle (in degrees) measured clockwise. This is michael@0: treated mod 360. michael@0: @param forceMoveTo If true, always begin a new contour with the arc michael@0: */ michael@0: void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, michael@0: bool forceMoveTo); michael@0: michael@0: /** Append a line and arc to the current path. This is the same as the michael@0: PostScript call "arct". michael@0: */ michael@0: void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, michael@0: SkScalar radius); michael@0: michael@0: /** Append a line and arc to the current path. This is the same as the michael@0: PostScript call "arct". michael@0: */ michael@0: void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) { michael@0: this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); michael@0: } michael@0: michael@0: /** Close the current contour. If the current point is not equal to the michael@0: first point of the contour, a line segment is automatically added. michael@0: */ michael@0: void close(); michael@0: michael@0: enum Direction { michael@0: /** Direction either has not been or could not be computed */ michael@0: kUnknown_Direction, michael@0: /** clockwise direction for adding closed contours */ michael@0: kCW_Direction, michael@0: /** counter-clockwise direction for adding closed contours */ michael@0: kCCW_Direction, michael@0: }; michael@0: michael@0: /** michael@0: * Return the opposite of the specified direction. kUnknown is its own michael@0: * opposite. michael@0: */ michael@0: static Direction OppositeDirection(Direction dir) { michael@0: static const Direction gOppositeDir[] = { michael@0: kUnknown_Direction, kCCW_Direction, kCW_Direction michael@0: }; michael@0: return gOppositeDir[dir]; michael@0: } michael@0: michael@0: /** michael@0: * Returns whether or not a fill type is inverted michael@0: * michael@0: * kWinding_FillType -> false michael@0: * kEvenOdd_FillType -> false michael@0: * kInverseWinding_FillType -> true michael@0: * kInverseEvenOdd_FillType -> true michael@0: */ michael@0: static bool IsInverseFillType(FillType fill) { michael@0: SK_COMPILE_ASSERT(0 == kWinding_FillType, fill_type_mismatch); michael@0: SK_COMPILE_ASSERT(1 == kEvenOdd_FillType, fill_type_mismatch); michael@0: SK_COMPILE_ASSERT(2 == kInverseWinding_FillType, fill_type_mismatch); michael@0: SK_COMPILE_ASSERT(3 == kInverseEvenOdd_FillType, fill_type_mismatch); michael@0: return (fill & 2) != 0; michael@0: } michael@0: michael@0: /** michael@0: * Returns the equivalent non-inverted fill type to the given fill type michael@0: * michael@0: * kWinding_FillType -> kWinding_FillType michael@0: * kEvenOdd_FillType -> kEvenOdd_FillType michael@0: * kInverseWinding_FillType -> kWinding_FillType michael@0: * kInverseEvenOdd_FillType -> kEvenOdd_FillType michael@0: */ michael@0: static FillType ConvertToNonInverseFillType(FillType fill) { michael@0: SK_COMPILE_ASSERT(0 == kWinding_FillType, fill_type_mismatch); michael@0: SK_COMPILE_ASSERT(1 == kEvenOdd_FillType, fill_type_mismatch); michael@0: SK_COMPILE_ASSERT(2 == kInverseWinding_FillType, fill_type_mismatch); michael@0: SK_COMPILE_ASSERT(3 == kInverseEvenOdd_FillType, fill_type_mismatch); michael@0: return (FillType)(fill & 1); michael@0: } michael@0: michael@0: /** michael@0: * Tries to quickly compute the direction of the first non-degenerate michael@0: * contour. If it can be computed, return true and set dir to that michael@0: * direction. If it cannot be (quickly) determined, return false and ignore michael@0: * the dir parameter. If the direction was determined, it is cached to make michael@0: * subsequent calls return quickly. michael@0: */ michael@0: bool cheapComputeDirection(Direction* dir) const; michael@0: michael@0: /** michael@0: * Returns true if the path's direction can be computed via michael@0: * cheapComputDirection() and if that computed direction matches the michael@0: * specified direction. If dir is kUnknown, returns true if the direction michael@0: * cannot be computed. michael@0: */ michael@0: bool cheapIsDirection(Direction dir) const { michael@0: Direction computedDir = kUnknown_Direction; michael@0: (void)this->cheapComputeDirection(&computedDir); michael@0: return computedDir == dir; michael@0: } michael@0: michael@0: enum PathAsRect { michael@0: /** The path can not draw the same as its bounds. */ michael@0: kNone_PathAsRect, michael@0: /** The path draws the same as its bounds when filled. */ michael@0: kFill_PathAsRect, michael@0: /** The path draws the same as its bounds when stroked or filled. */ michael@0: kStroke_PathAsRect, michael@0: }; michael@0: michael@0: /** Returns kFill_PathAsRect or kStroke_PathAsRect if drawing the path (either filled or michael@0: stroked) will be equivalent to filling/stroking the path's bounding rect. If michael@0: either is true, and direction is not null, sets the direction of the contour. If the michael@0: path is not drawn equivalent to a rect, returns kNone_PathAsRect and ignores direction. michael@0: michael@0: @param direction If not null, set to the contour's direction when it is drawn as a rect michael@0: @return the path's PathAsRect type michael@0: */ michael@0: PathAsRect asRect(Direction* direction = NULL) const; michael@0: michael@0: /** Returns true if the path specifies a rectangle. If so, and if isClosed is michael@0: not null, set isClosed to true if the path is closed. Also, if returning true michael@0: and direction is not null, return the rect direction. If the path does not michael@0: specify a rectangle, return false and ignore isClosed and direction. michael@0: michael@0: @param isClosed If not null, set to true if the path is closed michael@0: @param direction If not null, set to the rectangle's direction michael@0: @return true if the path specifies a rectangle michael@0: */ michael@0: bool isRect(bool* isClosed, Direction* direction) const; michael@0: michael@0: /** Returns true if the path specifies a pair of nested rectangles. If so, and if michael@0: rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner michael@0: rectangle. If so, and dirs is not null, set dirs[0] to the direction of michael@0: the outer rectangle and dirs[1] to the direction of the inner rectangle. If michael@0: the path does not specify a pair of nested rectangles, return michael@0: false and ignore rect and dirs. michael@0: michael@0: @param rect If not null, returns the path as a pair of nested rectangles michael@0: @param dirs If not null, returns the direction of the rects michael@0: @return true if the path describes a pair of nested rectangles michael@0: */ michael@0: bool isNestedRects(SkRect rect[2], Direction dirs[2] = NULL) const; michael@0: michael@0: /** michael@0: * Add a closed rectangle contour to the path michael@0: * @param rect The rectangle to add as a closed contour to the path michael@0: * @param dir The direction to wind the rectangle's contour. Cannot be michael@0: * kUnknown_Direction. michael@0: */ michael@0: void addRect(const SkRect& rect, Direction dir = kCW_Direction); michael@0: michael@0: /** michael@0: * Add a closed rectangle contour to the path michael@0: * michael@0: * @param left The left side of a rectangle to add as a closed contour michael@0: * to the path michael@0: * @param top The top of a rectangle to add as a closed contour to the michael@0: * path michael@0: * @param right The right side of a rectangle to add as a closed contour michael@0: * to the path michael@0: * @param bottom The bottom of a rectangle to add as a closed contour to michael@0: * the path michael@0: * @param dir The direction to wind the rectangle's contour. Cannot be michael@0: * kUnknown_Direction. michael@0: */ michael@0: void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, michael@0: Direction dir = kCW_Direction); michael@0: michael@0: /** michael@0: * Add a closed oval contour to the path michael@0: * michael@0: * @param oval The bounding oval to add as a closed contour to the path michael@0: * @param dir The direction to wind the oval's contour. Cannot be michael@0: * kUnknown_Direction. michael@0: */ michael@0: void addOval(const SkRect& oval, Direction dir = kCW_Direction); michael@0: michael@0: /** michael@0: * Add a closed circle contour to the path michael@0: * michael@0: * @param x The x-coordinate of the center of a circle to add as a michael@0: * closed contour to the path michael@0: * @param y The y-coordinate of the center of a circle to add as a michael@0: * closed contour to the path michael@0: * @param radius The radius of a circle to add as a closed contour to the michael@0: * path michael@0: * @param dir The direction to wind the circle's contour. Cannot be michael@0: * kUnknown_Direction. michael@0: */ michael@0: void addCircle(SkScalar x, SkScalar y, SkScalar radius, michael@0: Direction dir = kCW_Direction); michael@0: michael@0: /** Add the specified arc to the path as a new contour. michael@0: michael@0: @param oval The bounds of oval used to define the size of the arc michael@0: @param startAngle Starting angle (in degrees) where the arc begins michael@0: @param sweepAngle Sweep angle (in degrees) measured clockwise michael@0: */ michael@0: void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle); michael@0: michael@0: /** michael@0: * Add a closed round-rectangle contour to the path michael@0: * @param rect The bounds of a round-rectangle to add as a closed contour michael@0: * @param rx The x-radius of the rounded corners on the round-rectangle michael@0: * @param ry The y-radius of the rounded corners on the round-rectangle michael@0: * @param dir The direction to wind the rectangle's contour. Cannot be michael@0: * kUnknown_Direction. michael@0: */ michael@0: void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, michael@0: Direction dir = kCW_Direction); michael@0: michael@0: /** michael@0: * Add a closed round-rectangle contour to the path. Each corner receives michael@0: * two radius values [X, Y]. The corners are ordered top-left, top-right, michael@0: * bottom-right, bottom-left. michael@0: * @param rect The bounds of a round-rectangle to add as a closed contour michael@0: * @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner michael@0: * @param dir The direction to wind the rectangle's contour. Cannot be michael@0: * kUnknown_Direction. michael@0: * Note: The radii here now go through the same constraint handling as the michael@0: * SkRRect radii (i.e., either radii at a corner being 0 implies a michael@0: * sqaure corner and oversized radii are proportionally scaled down). michael@0: */ michael@0: void addRoundRect(const SkRect& rect, const SkScalar radii[], michael@0: Direction dir = kCW_Direction); michael@0: michael@0: /** michael@0: * Add an SkRRect contour to the path michael@0: * @param rrect The rounded rect to add as a closed contour michael@0: * @param dir The winding direction for the new contour. Cannot be michael@0: * kUnknown_Direction. michael@0: */ michael@0: void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction); michael@0: michael@0: /** michael@0: * Add a new contour made of just lines. This is just a fast version of michael@0: * the following: michael@0: * this->moveTo(pts[0]); michael@0: * for (int i = 1; i < count; ++i) { michael@0: * this->lineTo(pts[i]); michael@0: * } michael@0: * if (close) { michael@0: * this->close(); michael@0: * } michael@0: */ michael@0: void addPoly(const SkPoint pts[], int count, bool close); michael@0: michael@0: enum AddPathMode { michael@0: /** Source path contours are added as new contours. michael@0: */ michael@0: kAppend_AddPathMode, michael@0: /** Path is added by extending the last contour of the destination path michael@0: with the first contour of the source path. If the last contour of michael@0: the destination path is closed, then it will not be extended. michael@0: Instead, the start of source path will be extended by a straight michael@0: line to the end point of the destination path. michael@0: */ michael@0: kExtend_AddPathMode michael@0: }; michael@0: michael@0: /** Add a copy of src to the path, offset by (dx,dy) michael@0: @param src The path to add as a new contour michael@0: @param dx The amount to translate the path in X as it is added michael@0: @param dx The amount to translate the path in Y as it is added michael@0: */ michael@0: void addPath(const SkPath& src, SkScalar dx, SkScalar dy, michael@0: AddPathMode mode = kAppend_AddPathMode); michael@0: michael@0: /** Add a copy of src to the path michael@0: */ michael@0: void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) { michael@0: SkMatrix m; michael@0: m.reset(); michael@0: this->addPath(src, m, mode); michael@0: } michael@0: michael@0: /** Add a copy of src to the path, transformed by matrix michael@0: @param src The path to add as a new contour michael@0: @param matrix Transform applied to src michael@0: @param mode Determines how path is added michael@0: */ michael@0: void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode); michael@0: michael@0: /** michael@0: * Same as addPath(), but reverses the src input michael@0: */ michael@0: void reverseAddPath(const SkPath& src); michael@0: michael@0: /** Offset the path by (dx,dy), returning true on success michael@0: michael@0: @param dx The amount in the X direction to offset the entire path michael@0: @param dy The amount in the Y direction to offset the entire path michael@0: @param dst The translated path is written here michael@0: */ michael@0: void offset(SkScalar dx, SkScalar dy, SkPath* dst) const; michael@0: michael@0: /** Offset the path by (dx,dy), returning true on success michael@0: michael@0: @param dx The amount in the X direction to offset the entire path michael@0: @param dy The amount in the Y direction to offset the entire path michael@0: */ michael@0: void offset(SkScalar dx, SkScalar dy) { michael@0: this->offset(dx, dy, this); michael@0: } michael@0: michael@0: /** Transform the points in this path by matrix, and write the answer into michael@0: dst. michael@0: michael@0: @param matrix The matrix to apply to the path michael@0: @param dst The transformed path is written here michael@0: */ michael@0: void transform(const SkMatrix& matrix, SkPath* dst) const; michael@0: michael@0: /** Transform the points in this path by matrix michael@0: michael@0: @param matrix The matrix to apply to the path michael@0: */ michael@0: void transform(const SkMatrix& matrix) { michael@0: this->transform(matrix, this); michael@0: } michael@0: michael@0: /** Return the last point on the path. If no points have been added, (0,0) michael@0: is returned. If there are no points, this returns false, otherwise it michael@0: returns true. michael@0: michael@0: @param lastPt The last point on the path is returned here michael@0: */ michael@0: bool getLastPt(SkPoint* lastPt) const; michael@0: michael@0: /** Set the last point on the path. If no points have been added, michael@0: moveTo(x,y) is automatically called. michael@0: michael@0: @param x The new x-coordinate for the last point michael@0: @param y The new y-coordinate for the last point michael@0: */ michael@0: void setLastPt(SkScalar x, SkScalar y); michael@0: michael@0: /** Set the last point on the path. If no points have been added, moveTo(p) michael@0: is automatically called. michael@0: michael@0: @param p The new location for the last point michael@0: */ michael@0: void setLastPt(const SkPoint& p) { michael@0: this->setLastPt(p.fX, p.fY); michael@0: } michael@0: michael@0: enum SegmentMask { michael@0: kLine_SegmentMask = 1 << 0, michael@0: kQuad_SegmentMask = 1 << 1, michael@0: kConic_SegmentMask = 1 << 2, michael@0: kCubic_SegmentMask = 1 << 3, michael@0: }; michael@0: michael@0: /** michael@0: * Returns a mask, where each bit corresponding to a SegmentMask is michael@0: * set if the path contains 1 or more segments of that type. michael@0: * Returns 0 for an empty path (no segments). michael@0: */ michael@0: uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); } michael@0: michael@0: enum Verb { michael@0: kMove_Verb, //!< iter.next returns 1 point michael@0: kLine_Verb, //!< iter.next returns 2 points michael@0: kQuad_Verb, //!< iter.next returns 3 points michael@0: kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight() michael@0: kCubic_Verb, //!< iter.next returns 4 points michael@0: kClose_Verb, //!< iter.next returns 1 point (contour's moveTo pt) michael@0: kDone_Verb, //!< iter.next returns 0 points michael@0: }; michael@0: michael@0: /** Iterate through all of the segments (lines, quadratics, cubics) of michael@0: each contours in a path. michael@0: michael@0: The iterator cleans up the segments along the way, removing degenerate michael@0: segments and adding close verbs where necessary. When the forceClose michael@0: argument is provided, each contour (as defined by a new starting michael@0: move command) will be completed with a close verb regardless of the michael@0: contour's contents. michael@0: */ michael@0: class SK_API Iter { michael@0: public: michael@0: Iter(); michael@0: Iter(const SkPath&, bool forceClose); michael@0: michael@0: void setPath(const SkPath&, bool forceClose); michael@0: michael@0: /** Return the next verb in this iteration of the path. When all michael@0: segments have been visited, return kDone_Verb. michael@0: michael@0: @param pts The points representing the current verb and/or segment michael@0: @param doConsumeDegerates If true, first scan for segments that are michael@0: deemed degenerate (too short) and skip those. michael@0: @return The verb for the current segment michael@0: */ michael@0: Verb next(SkPoint pts[4], bool doConsumeDegerates = true) { michael@0: if (doConsumeDegerates) { michael@0: this->consumeDegenerateSegments(); michael@0: } michael@0: return this->doNext(pts); michael@0: } michael@0: michael@0: /** michael@0: * Return the weight for the current conic. Only valid if the current michael@0: * segment return by next() was a conic. michael@0: */ michael@0: SkScalar conicWeight() const { return *fConicWeights; } michael@0: michael@0: /** If next() returns kLine_Verb, then this query returns true if the michael@0: line was the result of a close() command (i.e. the end point is the michael@0: initial moveto for this contour). If next() returned a different michael@0: verb, this returns an undefined value. michael@0: michael@0: @return If the last call to next() returned kLine_Verb, return true michael@0: if it was the result of an explicit close command. michael@0: */ michael@0: bool isCloseLine() const { return SkToBool(fCloseLine); } michael@0: michael@0: /** Returns true if the current contour is closed (has a kClose_Verb) michael@0: @return true if the current contour is closed (has a kClose_Verb) michael@0: */ michael@0: bool isClosedContour() const; michael@0: michael@0: private: michael@0: const SkPoint* fPts; michael@0: const uint8_t* fVerbs; michael@0: const uint8_t* fVerbStop; michael@0: const SkScalar* fConicWeights; michael@0: SkPoint fMoveTo; michael@0: SkPoint fLastPt; michael@0: SkBool8 fForceClose; michael@0: SkBool8 fNeedClose; michael@0: SkBool8 fCloseLine; michael@0: SkBool8 fSegmentState; michael@0: michael@0: inline const SkPoint& cons_moveTo(); michael@0: Verb autoClose(SkPoint pts[2]); michael@0: void consumeDegenerateSegments(); michael@0: Verb doNext(SkPoint pts[4]); michael@0: }; michael@0: michael@0: /** Iterate through the verbs in the path, providing the associated points. michael@0: */ michael@0: class SK_API RawIter { michael@0: public: michael@0: RawIter(); michael@0: RawIter(const SkPath&); michael@0: michael@0: void setPath(const SkPath&); michael@0: michael@0: /** Return the next verb in this iteration of the path. When all michael@0: segments have been visited, return kDone_Verb. michael@0: michael@0: @param pts The points representing the current verb and/or segment michael@0: This must not be NULL. michael@0: @return The verb for the current segment michael@0: */ michael@0: Verb next(SkPoint pts[4]); michael@0: michael@0: SkScalar conicWeight() const { return *fConicWeights; } michael@0: michael@0: private: michael@0: const SkPoint* fPts; michael@0: const uint8_t* fVerbs; michael@0: const uint8_t* fVerbStop; michael@0: const SkScalar* fConicWeights; michael@0: SkPoint fMoveTo; michael@0: SkPoint fLastPt; michael@0: }; michael@0: michael@0: /** michael@0: * Returns true if the point { x, y } is contained by the path, taking into michael@0: * account the FillType. michael@0: */ michael@0: bool contains(SkScalar x, SkScalar y) const; michael@0: michael@0: void dump(bool forceClose, const char title[] = NULL) const; michael@0: void dump() const; michael@0: michael@0: /** michael@0: * Write the path to the buffer, and return the number of bytes written. michael@0: * If buffer is NULL, it still returns the number of bytes. michael@0: */ michael@0: size_t writeToMemory(void* buffer) const; michael@0: /** michael@0: * Initializes the path from the buffer michael@0: * michael@0: * @param buffer Memory to read from michael@0: * @param length Amount of memory available in the buffer michael@0: * @return number of bytes read (must be a multiple of 4) or michael@0: * 0 if there was not enough memory available michael@0: */ michael@0: size_t readFromMemory(const void* buffer, size_t length); michael@0: michael@0: /** Returns a non-zero, globally unique value corresponding to the set of verbs michael@0: and points in the path (but not the fill type [except on Android skbug.com/1762]). michael@0: Each time the path is modified, a different generation ID will be returned. michael@0: */ michael@0: uint32_t getGenerationID() const; michael@0: michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762) michael@0: const SkPath* getSourcePath() const; michael@0: void setSourcePath(const SkPath* path); michael@0: #else michael@0: static const int kPathRefGenIDBitCnt = 32; michael@0: #endif michael@0: michael@0: SkDEBUGCODE(void validate() const;) michael@0: michael@0: private: michael@0: enum SerializationOffsets { michael@0: // 1 free bit at 29 michael@0: kUnused1_SerializationShift = 28, // 1 free bit michael@0: kDirection_SerializationShift = 26, // requires 2 bits michael@0: kUnused2_SerializationShift = 25, // 1 free bit michael@0: // 1 free bit at 24 michael@0: kConvexity_SerializationShift = 16, // requires 8 bits michael@0: kFillType_SerializationShift = 8, // requires 8 bits michael@0: // 8 free bits at 0 michael@0: }; michael@0: michael@0: SkAutoTUnref fPathRef; michael@0: michael@0: int fLastMoveToIndex; michael@0: uint8_t fFillType; michael@0: mutable uint8_t fConvexity; michael@0: mutable uint8_t fDirection; michael@0: #ifdef SK_BUILD_FOR_ANDROID michael@0: const SkPath* fSourcePath; michael@0: #endif michael@0: michael@0: /** Resets all fields other than fPathRef to their initial 'empty' values. michael@0: * Assumes the caller has already emptied fPathRef. michael@0: * On Android increments fGenerationID without reseting it. michael@0: */ michael@0: void resetFields(); michael@0: michael@0: /** Sets all fields other than fPathRef to the values in 'that'. michael@0: * Assumes the caller has already set fPathRef. michael@0: * Doesn't change fGenerationID or fSourcePath on Android. michael@0: */ michael@0: void copyFields(const SkPath& that); michael@0: michael@0: friend class Iter; michael@0: michael@0: friend class SkPathStroker; michael@0: michael@0: /* Append, in reverse order, the first contour of path, ignoring path's michael@0: last point. If no moveTo() call has been made for this contour, the michael@0: first point is automatically set to (0,0). michael@0: */ michael@0: void reversePathTo(const SkPath&); michael@0: michael@0: // called before we add points for lineTo, quadTo, cubicTo, checking to see michael@0: // if we need to inject a leading moveTo first michael@0: // michael@0: // SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0) michael@0: // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo) michael@0: // michael@0: inline void injectMoveToIfNeeded(); michael@0: michael@0: inline bool hasOnlyMoveTos() const; michael@0: michael@0: Convexity internalGetConvexity() const; michael@0: michael@0: bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts, michael@0: bool* isClosed, Direction* direction) const; michael@0: michael@0: /** Returns if the path can return a bound at no cost (true) or will have to michael@0: perform some computation (false). michael@0: */ michael@0: bool hasComputedBounds() const { michael@0: SkDEBUGCODE(this->validate();) michael@0: return fPathRef->hasComputedBounds(); michael@0: } michael@0: michael@0: michael@0: // 'rect' needs to be sorted michael@0: void setBounds(const SkRect& rect) { michael@0: SkPathRef::Editor ed(&fPathRef); michael@0: michael@0: ed.setBounds(rect); michael@0: } michael@0: michael@0: friend class SkAutoPathBoundsUpdate; michael@0: friend class SkAutoDisableOvalCheck; michael@0: friend class SkAutoDisableDirectionCheck; michael@0: friend class SkBench_AddPathTest; // perf test reversePathTo michael@0: friend class PathTest_Private; // unit test reversePathTo michael@0: }; michael@0: michael@0: #endif