| /* |
| * Copyright 2006 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SkPath_DEFINED |
| #define SkPath_DEFINED |
| |
| #include "SkMatrix.h" |
| #include "../private/SkPathRef.h" |
| |
| class SkAutoPathBoundsUpdate; |
| class SkRRect; |
| class SkWStream; |
| |
| /** \class SkPath |
| |
| The SkPath class encapsulates compound (multiple contour) geometric paths |
| consisting of straight line segments, quadratic curves, and cubic curves. |
| |
| SkPath is not thread safe unless you've first called SkPath::updateBoundsCache(). |
| */ |
| class SK_API SkPath { |
| public: |
| enum Direction { |
| /** clockwise direction for adding closed contours */ |
| kCW_Direction, |
| /** counter-clockwise direction for adding closed contours */ |
| kCCW_Direction, |
| }; |
| |
| SkPath(); |
| SkPath(const SkPath& path); |
| ~SkPath(); |
| |
| SkPath& operator=(const SkPath& path); |
| // mac chromium dbg requires SK_API to make operator== visible |
| friend SK_API bool operator==(const SkPath& a, const SkPath& b); |
| friend bool operator!=(const SkPath& a, const SkPath& b) { |
| return !(a == b); |
| } |
| |
| /** Return true if the paths contain an equal array of verbs and weights. Paths |
| * with equal verb counts can be readily interpolated. If the paths contain one |
| * or more conics, the conics' weights must also match. |
| * |
| * @param compare The path to compare. |
| * |
| * @return true if the paths have the same verbs and weights. |
| */ |
| bool isInterpolatable(const SkPath& compare) const; |
| |
| /** Interpolate between two paths with same-sized point arrays. |
| * The out path contains the verbs and weights of this path. |
| * The out points are a weighted average of this path and the ending path. |
| * |
| * @param ending The path to interpolate between. |
| * @param weight The weight, from 0 to 1. The output points are set to |
| * (this->points * weight) + ending->points * (1 - weight). |
| * @return true if the paths could be interpolated. |
| */ |
| bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const; |
| |
| #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK |
| /** Returns true if the caller is the only owner of the underlying path data */ |
| bool unique() const { return fPathRef->unique(); } |
| #endif |
| |
| enum FillType { |
| /** Specifies that "inside" is computed by a non-zero sum of signed |
| edge crossings |
| */ |
| kWinding_FillType, |
| /** Specifies that "inside" is computed by an odd number of edge |
| crossings |
| */ |
| kEvenOdd_FillType, |
| /** Same as Winding, but draws outside of the path, rather than inside |
| */ |
| kInverseWinding_FillType, |
| /** Same as EvenOdd, but draws outside of the path, rather than inside |
| */ |
| kInverseEvenOdd_FillType |
| }; |
| |
| /** Return the path's fill type. This is used to define how "inside" is |
| computed. The default value is kWinding_FillType. |
| |
| @return the path's fill type |
| */ |
| FillType getFillType() const { return (FillType)fFillType; } |
| |
| /** Set the path's fill type. This is used to define how "inside" is |
| computed. The default value is kWinding_FillType. |
| |
| @param ft The new fill type for this path |
| */ |
| void setFillType(FillType ft) { |
| fFillType = SkToU8(ft); |
| } |
| |
| /** Returns true if the filltype is one of the Inverse variants */ |
| bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); } |
| |
| /** |
| * Toggle between inverse and normal filltypes. This reverse the return |
| * value of isInverseFillType() |
| */ |
| void toggleInverseFillType() { |
| fFillType ^= 2; |
| } |
| |
| enum Convexity { |
| kUnknown_Convexity, |
| kConvex_Convexity, |
| kConcave_Convexity |
| }; |
| |
| /** |
| * Return the path's convexity, as stored in the path. If it is currently unknown, |
| * then this function will attempt to compute the convexity (and cache the result). |
| */ |
| Convexity getConvexity() const { |
| if (kUnknown_Convexity != fConvexity) { |
| return static_cast<Convexity>(fConvexity); |
| } else { |
| return this->internalGetConvexity(); |
| } |
| } |
| |
| /** |
| * Return the currently cached value for convexity, even if that is set to |
| * kUnknown_Convexity. Note: getConvexity() will automatically call |
| * ComputeConvexity and cache its return value if the current setting is |
| * kUnknown. |
| */ |
| Convexity getConvexityOrUnknown() const { return (Convexity)fConvexity; } |
| |
| /** |
| * Store a convexity setting in the path. There is no automatic check to |
| * see if this value actually agrees with the return value that would be |
| * computed by getConvexity(). |
| * |
| * Note: even if this is set to a "known" value, if the path is later |
| * changed (e.g. lineTo(), addRect(), etc.) then the cached value will be |
| * reset to kUnknown_Convexity. |
| */ |
| void setConvexity(Convexity convexity); |
| |
| /** |
| * Returns true if the path is flagged as being convex. This is not a |
| * confirmed by any analysis, it is just the value set earlier. |
| */ |
| bool isConvex() const { |
| return kConvex_Convexity == this->getConvexity(); |
| } |
| |
| /** |
| * Set the isConvex flag to true or false. Convex paths may draw faster if |
| * this flag is set, though setting this to true on a path that is in fact |
| * not convex can give undefined results when drawn. Paths default to |
| * isConvex == false |
| */ |
| SK_ATTR_DEPRECATED("use setConvexity") |
| void setIsConvex(bool isConvex) { |
| this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity); |
| } |
| |
| /** Returns true if the path is an oval. |
| * |
| * @param rect returns the bounding rect of this oval. It's a circle |
| * if the height and width are the same. |
| * @param dir is the oval CCW (or CW if false). |
| * @param start indicates where the contour starts on the oval (see |
| * SkPath::addOval for intepretation of the index). |
| * @return true if this path is an oval. |
| * Tracking whether a path is an oval is considered an |
| * optimization for performance and so some paths that are in |
| * fact ovals can report false. |
| */ |
| bool isOval(SkRect* rect, Direction* dir = nullptr, |
| unsigned* start = nullptr) const { |
| bool isCCW = false; |
| bool result = fPathRef->isOval(rect, &isCCW, start); |
| if (dir && result) { |
| *dir = isCCW ? kCCW_Direction : kCW_Direction; |
| } |
| return result; |
| } |
| |
| /** Returns true if the path is a round rect. |
| * |
| * @param rrect Returns the bounding rect and radii of this round rect. |
| * @param dir is the rrect CCW (or CW if false). |
| * @param start indicates where the contour starts on the rrect (see |
| * SkPath::addRRect for intepretation of the index). |
| * |
| * @return true if this path is a round rect. |
| * Tracking whether a path is a round rect is considered an |
| * optimization for performance and so some paths that are in |
| * fact round rects can report false. |
| */ |
| bool isRRect(SkRRect* rrect, Direction* dir = nullptr, |
| unsigned* start = nullptr) const { |
| bool isCCW = false; |
| bool result = fPathRef->isRRect(rrect, &isCCW, start); |
| if (dir && result) { |
| *dir = isCCW ? kCCW_Direction : kCW_Direction; |
| } |
| return result; |
| } |
| |
| /** Clear any lines and curves from the path, making it empty. This frees up |
| internal storage associated with those segments. |
| On Android, does not change fSourcePath. |
| */ |
| void reset(); |
| |
| /** Similar to reset(), in that all lines and curves are removed from the |
| path. However, any internal storage for those lines/curves is retained, |
| making reuse of the path potentially faster. |
| On Android, does not change fSourcePath. |
| */ |
| void rewind(); |
| |
| /** Returns true if the path is empty (contains no lines or curves) |
| |
| @return true if the path is empty (contains no lines or curves) |
| */ |
| bool isEmpty() const { |
| SkDEBUGCODE(this->validate();) |
| return 0 == fPathRef->countVerbs(); |
| } |
| |
| /** Return true if the last contour of this path ends with a close verb. |
| */ |
| bool isLastContourClosed() const; |
| |
| /** |
| * Returns true if all of the points in this path are finite, meaning there |
| * are no infinities and no NaNs. |
| */ |
| bool isFinite() const { |
| SkDEBUGCODE(this->validate();) |
| return fPathRef->isFinite(); |
| } |
| |
| /** Returns true if the path is volatile (i.e. should not be cached by devices.) |
| */ |
| bool isVolatile() const { |
| return SkToBool(fIsVolatile); |
| } |
| |
| /** Specify whether this path is volatile. Paths are not volatile by |
| default. Temporary paths that are discarded or modified after use should be |
| marked as volatile. This provides a hint to the device that the path |
| should not be cached. Providing this hint when appropriate can |
| improve performance by avoiding unnecessary overhead and resource |
| consumption on the device. |
| */ |
| void setIsVolatile(bool isVolatile) { |
| fIsVolatile = isVolatile; |
| } |
| |
| /** Test a line for zero length |
| |
| @return true if the line is of zero length; otherwise false. |
| */ |
| static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) { |
| return exact ? p1 == p2 : p1.equalsWithinTolerance(p2); |
| } |
| |
| /** Test a quad for zero length |
| |
| @return true if the quad is of zero length; otherwise false. |
| */ |
| static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, |
| const SkPoint& p3, bool exact) { |
| return exact ? p1 == p2 && p2 == p3 : p1.equalsWithinTolerance(p2) && |
| p2.equalsWithinTolerance(p3); |
| } |
| |
| /** Test a cubic curve for zero length |
| |
| @return true if the cubic is of zero length; otherwise false. |
| */ |
| static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, |
| const SkPoint& p3, const SkPoint& p4, bool exact) { |
| return exact ? p1 == p2 && p2 == p3 && p3 == p4 : p1.equalsWithinTolerance(p2) && |
| p2.equalsWithinTolerance(p3) && |
| p3.equalsWithinTolerance(p4); |
| } |
| |
| /** |
| * Returns true if the path specifies a single line (i.e. it contains just |
| * a moveTo and a lineTo). If so, and line[] is not null, it sets the 2 |
| * points in line[] to the end-points of the line. If the path is not a |
| * line, returns false and ignores line[]. |
| */ |
| bool isLine(SkPoint line[2]) const; |
| |
| /** Return the number of points in the path |
| */ |
| int countPoints() const; |
| |
| /** Return the point at the specified index. If the index is out of range |
| (i.e. is not 0 <= index < countPoints()) then the returned coordinates |
| will be (0,0) |
| */ |
| SkPoint getPoint(int index) const; |
| |
| /** Returns the number of points in the path. Up to max points are copied. |
| |
| @param points If not null, receives up to max points |
| @param max The maximum number of points to copy into points |
| @return the actual number of points in the path |
| */ |
| int getPoints(SkPoint points[], int max) const; |
| |
| /** Return the number of verbs in the path |
| */ |
| int countVerbs() const; |
| |
| /** Returns the number of verbs in the path. Up to max verbs are copied. The |
| verbs are copied as one byte per verb. |
| |
| @param verbs If not null, receives up to max verbs |
| @param max The maximum number of verbs to copy into verbs |
| @return the actual number of verbs in the path |
| */ |
| int getVerbs(uint8_t verbs[], int max) const; |
| |
| //! Swap contents of this and other. Guaranteed not to throw |
| void swap(SkPath& other); |
| |
| /** |
| * Returns the bounds of the path's points. If the path contains zero points/verbs, this |
| * will return the "empty" rect [0, 0, 0, 0]. |
| * Note: this bounds may be larger than the actual shape, since curves |
| * do not extend as far as their control points. Additionally this bound encompases all points, |
| * even isolated moveTos either preceeding or following the last non-degenerate contour. |
| */ |
| const SkRect& getBounds() const { |
| return fPathRef->getBounds(); |
| } |
| |
| /** Calling this will, if the internal cache of the bounds is out of date, |
| update it so that subsequent calls to getBounds will be instantaneous. |
| This also means that any copies or simple transformations of the path |
| will inherit the cached bounds. |
| */ |
| void updateBoundsCache() const { |
| // for now, just calling getBounds() is sufficient |
| this->getBounds(); |
| } |
| |
| /** |
| * Computes a bounds that is conservatively "snug" around the path. This assumes that the |
| * path will be filled. It does not attempt to collapse away contours that are logically |
| * empty (e.g. moveTo(x, y) + lineTo(x, y)) but will include them in the calculation. |
| * |
| * It differs from getBounds() in that it will look at the snug bounds of curves, whereas |
| * getBounds() just returns the bounds of the control-points. Thus computing this may be |
| * slower than just calling getBounds(). |
| * |
| * If the path is empty (i.e. no points or verbs), it will return SkRect::MakeEmpty(). |
| */ |
| SkRect computeTightBounds() const; |
| |
| /** |
| * Does a conservative test to see whether a rectangle is inside a path. Currently it only |
| * will ever return true for single convex contour paths. The empty-status of the rect is not |
| * considered (e.g. a rect that is a point can be inside a path). Points or line segments where |
| * the rect edge touches the path border are not considered containment violations. |
| */ |
| bool conservativelyContainsRect(const SkRect& rect) const; |
| |
| // Construction methods |
| |
| /** Hint to the path to prepare for adding more points. This can allow the |
| path to more efficiently grow its storage. |
| |
| @param extraPtCount The number of extra points the path should |
| preallocate for. |
| */ |
| void incReserve(unsigned extraPtCount); |
| |
| /** Set the beginning of the next contour to the point (x,y). |
| |
| @param x The x-coordinate of the start of a new contour |
| @param y The y-coordinate of the start of a new contour |
| */ |
| void moveTo(SkScalar x, SkScalar y); |
| |
| /** Set the beginning of the next contour to the point |
| |
| @param p The start of a new contour |
| */ |
| void moveTo(const SkPoint& p) { |
| this->moveTo(p.fX, p.fY); |
| } |
| |
| /** Set the beginning of the next contour relative to the last point on the |
| previous contour. If there is no previous contour, this is treated the |
| same as moveTo(). |
| |
| @param dx The amount to add to the x-coordinate of the end of the |
| previous contour, to specify the start of a new contour |
| @param dy The amount to add to the y-coordinate of the end of the |
| previous contour, to specify the start of a new contour |
| */ |
| void rMoveTo(SkScalar dx, SkScalar dy); |
| |
| /** Add a line from the last point to the specified point (x,y). If no |
| moveTo() call has been made for this contour, the first point is |
| automatically set to (0,0). |
| |
| @param x The x-coordinate of the end of a line |
| @param y The y-coordinate of the end of a line |
| */ |
| void lineTo(SkScalar x, SkScalar y); |
| |
| /** Add a line from the last point to the specified point. If no moveTo() |
| call has been made for this contour, the first point is automatically |
| set to (0,0). |
| |
| @param p The end of a line |
| */ |
| void lineTo(const SkPoint& p) { |
| this->lineTo(p.fX, p.fY); |
| } |
| |
| /** Same as lineTo, but the coordinates are considered relative to the last |
| point on this contour. If there is no previous point, then a moveTo(0,0) |
| is inserted automatically. |
| |
| @param dx The amount to add to the x-coordinate of the previous point |
| on this contour, to specify a line |
| @param dy The amount to add to the y-coordinate of the previous point |
| on this contour, to specify a line |
| */ |
| void rLineTo(SkScalar dx, SkScalar dy); |
| |
| /** Add a quadratic bezier from the last point, approaching control point |
| (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for |
| this contour, the first point is automatically set to (0,0). |
| |
| @param x1 The x-coordinate of the control point on a quadratic curve |
| @param y1 The y-coordinate of the control point on a quadratic curve |
| @param x2 The x-coordinate of the end point on a quadratic curve |
| @param y2 The y-coordinate of the end point on a quadratic curve |
| */ |
| void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); |
| |
| /** Add a quadratic bezier from the last point, approaching control point |
| p1, and ending at p2. If no moveTo() call has been made for this |
| contour, the first point is automatically set to (0,0). |
| |
| @param p1 The control point on a quadratic curve |
| @param p2 The end point on a quadratic curve |
| */ |
| void quadTo(const SkPoint& p1, const SkPoint& p2) { |
| this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); |
| } |
| |
| /** Same as quadTo, but the coordinates are considered relative to the last |
| point on this contour. If there is no previous point, then a moveTo(0,0) |
| is inserted automatically. |
| |
| @param dx1 The amount to add to the x-coordinate of the last point on |
| this contour, to specify the control point of a quadratic curve |
| @param dy1 The amount to add to the y-coordinate of the last point on |
| this contour, to specify the control point of a quadratic curve |
| @param dx2 The amount to add to the x-coordinate of the last point on |
| this contour, to specify the end point of a quadratic curve |
| @param dy2 The amount to add to the y-coordinate of the last point on |
| this contour, to specify the end point of a quadratic curve |
| */ |
| void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); |
| |
| void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, |
| SkScalar w); |
| void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) { |
| this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w); |
| } |
| void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, |
| SkScalar w); |
| |
| /** Add a cubic bezier from the last point, approaching control points |
| (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been |
| made for this contour, the first point is automatically set to (0,0). |
| |
| @param x1 The x-coordinate of the 1st control point on a cubic curve |
| @param y1 The y-coordinate of the 1st control point on a cubic curve |
| @param x2 The x-coordinate of the 2nd control point on a cubic curve |
| @param y2 The y-coordinate of the 2nd control point on a cubic curve |
| @param x3 The x-coordinate of the end point on a cubic curve |
| @param y3 The y-coordinate of the end point on a cubic curve |
| */ |
| void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, |
| SkScalar x3, SkScalar y3); |
| |
| /** Add a cubic bezier from the last point, approaching control points p1 |
| and p2, and ending at p3. If no moveTo() call has been made for this |
| contour, the first point is automatically set to (0,0). |
| |
| @param p1 The 1st control point on a cubic curve |
| @param p2 The 2nd control point on a cubic curve |
| @param p3 The end point on a cubic curve |
| */ |
| void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) { |
| this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); |
| } |
| |
| /** Same as cubicTo, but the coordinates are considered relative to the |
| current point on this contour. If there is no previous point, then a |
| moveTo(0,0) is inserted automatically. |
| |
| @param x1 The amount to add to the x-coordinate of the last point on |
| this contour, to specify the 1st control point of a cubic curve |
| @param y1 The amount to add to the y-coordinate of the last point on |
| this contour, to specify the 1st control point of a cubic curve |
| @param x2 The amount to add to the x-coordinate of the last point on |
| this contour, to specify the 2nd control point of a cubic curve |
| @param y2 The amount to add to the y-coordinate of the last point on |
| this contour, to specify the 2nd control point of a cubic curve |
| @param x3 The amount to add to the x-coordinate of the last point on |
| this contour, to specify the end point of a cubic curve |
| @param y3 The amount to add to the y-coordinate of the last point on |
| this contour, to specify the end point of a cubic curve |
| */ |
| void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, |
| SkScalar x3, SkScalar y3); |
| |
| /** |
| * Append the specified arc to the path. If the start of the arc is different from the path's |
| * current last point, then an automatic lineTo() is added to connect the current contour |
| * to the start of the arc. However, if the path is empty, then we call moveTo() with |
| * the first point of the arc. The sweep angle is treated mod 360. |
| * |
| * @param oval The bounding oval defining the shape and size of the arc |
| * @param startAngle Starting angle (in degrees) where the arc begins |
| * @param sweepAngle Sweep angle (in degrees) measured clockwise. This is treated mod 360. |
| * @param forceMoveTo If true, always begin a new contour with the arc |
| */ |
| void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo); |
| |
| /** |
| * Append a line and arc to the current path. This is the same as the PostScript call "arct". |
| */ |
| void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius); |
| |
| /** Append a line and arc to the current path. This is the same as the |
| PostScript call "arct". |
| */ |
| void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) { |
| this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); |
| } |
| |
| enum ArcSize { |
| /** the smaller of the two possible SVG arcs. */ |
| kSmall_ArcSize, |
| /** the larger of the two possible SVG arcs. */ |
| kLarge_ArcSize, |
| }; |
| |
| /** |
| * Append an elliptical arc from the current point in the format used by SVG. |
| * The center of the ellipse is computed to satisfy the constraints below. |
| * |
| * @param rx,ry The radii in the x and y directions respectively. |
| * @param xAxisRotate The angle in degrees relative to the x-axis. |
| * @param largeArc Determines whether the smallest or largest arc possible |
| * is drawn. |
| * @param sweep Determines if the arc should be swept in an anti-clockwise or |
| * clockwise direction. Note that this enum value is opposite the SVG |
| * arc sweep value. |
| * @param x,y The destination coordinates. |
| */ |
| void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, |
| Direction sweep, SkScalar x, SkScalar y); |
| |
| void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep, |
| const SkPoint xy) { |
| this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY); |
| } |
| |
| /** Same as arcTo format used by SVG, but the destination coordinate is relative to the |
| * last point on this contour. If there is no previous point, then a |
| * moveTo(0,0) is inserted automatically. |
| * |
| * @param rx,ry The radii in the x and y directions respectively. |
| * @param xAxisRotate The angle in degrees relative to the x-axis. |
| * @param largeArc Determines whether the smallest or largest arc possible |
| * is drawn. |
| * @param sweep Determines if the arc should be swept in an anti-clockwise or |
| * clockwise direction. Note that this enum value is opposite the SVG |
| * arc sweep value. |
| * @param dx,dy The destination coordinates relative to the last point. |
| */ |
| void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, |
| Direction sweep, SkScalar dx, SkScalar dy); |
| |
| /** Close the current contour. If the current point is not equal to the |
| first point of the contour, a line segment is automatically added. |
| */ |
| void close(); |
| |
| /** |
| * Returns whether or not a fill type is inverted |
| * |
| * kWinding_FillType -> false |
| * kEvenOdd_FillType -> false |
| * kInverseWinding_FillType -> true |
| * kInverseEvenOdd_FillType -> true |
| */ |
| static bool IsInverseFillType(FillType fill) { |
| static_assert(0 == kWinding_FillType, "fill_type_mismatch"); |
| static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch"); |
| static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch"); |
| static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch"); |
| return (fill & 2) != 0; |
| } |
| |
| /** |
| * Returns the equivalent non-inverted fill type to the given fill type |
| * |
| * kWinding_FillType -> kWinding_FillType |
| * kEvenOdd_FillType -> kEvenOdd_FillType |
| * kInverseWinding_FillType -> kWinding_FillType |
| * kInverseEvenOdd_FillType -> kEvenOdd_FillType |
| */ |
| static FillType ConvertToNonInverseFillType(FillType fill) { |
| static_assert(0 == kWinding_FillType, "fill_type_mismatch"); |
| static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch"); |
| static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch"); |
| static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch"); |
| return (FillType)(fill & 1); |
| } |
| |
| /** |
| * Chop a conic into N quads, stored continguously in pts[], where |
| * N = 1 << pow2. The amount of storage needed is (1 + 2 * N) |
| */ |
| static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, |
| SkScalar w, SkPoint pts[], int pow2); |
| |
| /** |
| * Returns true if the path specifies a rectangle. |
| * |
| * If this returns false, then all output parameters are ignored, and left |
| * unchanged. If this returns true, then each of the output parameters |
| * are checked for NULL. If they are not, they return their value. |
| * |
| * @param rect If not null, set to the bounds of the rectangle. |
| * Note : this bounds may be smaller than the path's bounds, since it is just |
| * the bounds of the "drawable" parts of the path. e.g. a trailing MoveTo would |
| * be ignored in this rect, but not by the path's bounds |
| * @param isClosed If not null, set to true if the path is closed |
| * @param direction If not null, set to the rectangle's direction |
| * @return true if the path specifies a rectangle |
| */ |
| bool isRect(SkRect* rect, bool* isClosed = NULL, Direction* direction = NULL) const; |
| |
| /** Returns true if the path specifies a pair of nested rectangles, or would draw a |
| pair of nested rectangles when filled. If so, and if |
| rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner |
| rectangle. If so, and dirs is not null, set dirs[0] to the direction of |
| the outer rectangle and dirs[1] to the direction of the inner rectangle. If |
| the path does not specify a pair of nested rectangles, return |
| false and ignore rect and dirs. |
| |
| @param rect If not null, returns the path as a pair of nested rectangles |
| @param dirs If not null, returns the direction of the rects |
| @return true if the path describes a pair of nested rectangles |
| */ |
| bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = NULL) const; |
| |
| /** |
| * Add a closed rectangle contour to the path |
| * @param rect The rectangle to add as a closed contour to the path |
| * @param dir The direction to wind the rectangle's contour. |
| * |
| * Note: the contour initial point index is 0 (as defined below). |
| */ |
| void addRect(const SkRect& rect, Direction dir = kCW_Direction); |
| |
| /** |
| * Add a closed rectangle contour to the path |
| * @param rect The rectangle to add as a closed contour to the path |
| * @param dir The direction to wind the rectangle's contour. |
| * @param start Initial point of the contour (initial moveTo), expressed as |
| * a corner index, starting in the upper-left position, clock-wise: |
| * |
| * 0 1 |
| * *-------* |
| * | | |
| * *-------* |
| * 3 2 |
| */ |
| void addRect(const SkRect& rect, Direction dir, unsigned start); |
| |
| /** |
| * Add a closed rectangle contour to the path |
| * |
| * @param left The left side of a rectangle to add as a closed contour |
| * to the path |
| * @param top The top of a rectangle to add as a closed contour to the |
| * path |
| * @param right The right side of a rectangle to add as a closed contour |
| * to the path |
| * @param bottom The bottom of a rectangle to add as a closed contour to |
| * the path |
| * @param dir The direction to wind the rectangle's contour. |
| * |
| * Note: the contour initial point index is 0 (as defined above). |
| */ |
| void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, |
| Direction dir = kCW_Direction); |
| |
| /** |
| * Add a closed oval contour to the path |
| * |
| * @param oval The bounding oval to add as a closed contour to the path |
| * @param dir The direction to wind the oval's contour. |
| * |
| * Note: the contour initial point index is 1 (as defined below). |
| */ |
| void addOval(const SkRect& oval, Direction dir = kCW_Direction); |
| |
| /** |
| * Add a closed oval contour to the path |
| * |
| * @param oval The bounding oval to add as a closed contour to the path |
| * @param dir The direction to wind the oval's contour. |
| * @param start Initial point of the contour (initial moveTo), expressed |
| * as an ellipse vertex index, starting at the top, clock-wise |
| * (90/0/270/180deg order): |
| * |
| * 0 |
| * -*- |
| * | | |
| * 3 * * 1 |
| * | | |
| * -*- |
| * 2 |
| */ |
| void addOval(const SkRect& oval, Direction dir, unsigned start); |
| |
| /** |
| * Add a closed circle contour to the path. The circle contour begins at |
| * the right-most point (as though 1 were passed to addOval's 'start' param). |
| * |
| * @param x The x-coordinate of the center of a circle to add as a |
| * closed contour to the path |
| * @param y The y-coordinate of the center of a circle to add as a |
| * closed contour to the path |
| * @param radius The radius of a circle to add as a closed contour to the |
| * path |
| * @param dir The direction to wind the circle's contour. |
| */ |
| void addCircle(SkScalar x, SkScalar y, SkScalar radius, |
| Direction dir = kCW_Direction); |
| |
| /** Add the specified arc to the path as a new contour. |
| |
| @param oval The bounds of oval used to define the size of the arc |
| @param startAngle Starting angle (in degrees) where the arc begins |
| @param sweepAngle Sweep angle (in degrees) measured clockwise |
| */ |
| void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle); |
| |
| /** |
| * Add a closed round-rectangle contour to the path |
| * @param rect The bounds of a round-rectangle to add as a closed contour |
| * @param rx The x-radius of the rounded corners on the round-rectangle |
| * @param ry The y-radius of the rounded corners on the round-rectangle |
| * @param dir The direction to wind the rectangle's contour. |
| */ |
| void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, |
| Direction dir = kCW_Direction); |
| |
| /** |
| * Add a closed round-rectangle contour to the path. Each corner receives |
| * two radius values [X, Y]. The corners are ordered top-left, top-right, |
| * bottom-right, bottom-left. |
| * @param rect The bounds of a round-rectangle to add as a closed contour |
| * @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner |
| * @param dir The direction to wind the rectangle's contour. |
| * Note: The radii here now go through the same constraint handling as the |
| * SkRRect radii (i.e., either radii at a corner being 0 implies a |
| * sqaure corner and oversized radii are proportionally scaled down). |
| */ |
| void addRoundRect(const SkRect& rect, const SkScalar radii[], |
| Direction dir = kCW_Direction); |
| |
| /** |
| * Add an SkRRect contour to the path |
| * @param rrect The rounded rect to add as a closed contour |
| * @param dir The winding direction for the new contour. |
| * |
| * Note: the contour initial point index is either 6 (for dir == kCW_Direction) |
| * or 7 (for dir == kCCW_Direction), as defined below. |
| * |
| */ |
| void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction); |
| |
| /** |
| * Add an SkRRect contour to the path |
| * @param rrect The rounded rect to add as a closed contour |
| * @param dir The winding direction for the new contour. |
| * @param start Initial point of the contour (initial moveTo), expressed as |
| * an index of the radii minor/major points, ordered clock-wise: |
| * |
| * 0 1 |
| * *----* |
| * 7 * * 2 |
| * | | |
| * 6 * * 3 |
| * *----* |
| * 5 4 |
| */ |
| void addRRect(const SkRRect& rrect, Direction dir, unsigned start); |
| |
| /** |
| * Add a new contour made of just lines. This is just a fast version of |
| * the following: |
| * this->moveTo(pts[0]); |
| * for (int i = 1; i < count; ++i) { |
| * this->lineTo(pts[i]); |
| * } |
| * if (close) { |
| * this->close(); |
| * } |
| */ |
| void addPoly(const SkPoint pts[], int count, bool close); |
| |
| enum AddPathMode { |
| /** Source path contours are added as new contours. |
| */ |
| kAppend_AddPathMode, |
| /** Path is added by extending the last contour of the destination path |
| with the first contour of the source path. If the last contour of |
| the destination path is closed, then it will not be extended. |
| Instead, the start of source path will be extended by a straight |
| line to the end point of the destination path. |
| */ |
| kExtend_AddPathMode |
| }; |
| |
| /** Add a copy of src to the path, offset by (dx,dy) |
| @param src The path to add as a new contour |
| @param dx The amount to translate the path in X as it is added |
| @param dx The amount to translate the path in Y as it is added |
| */ |
| void addPath(const SkPath& src, SkScalar dx, SkScalar dy, |
| AddPathMode mode = kAppend_AddPathMode); |
| |
| /** Add a copy of src to the path |
| */ |
| void addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) { |
| SkMatrix m; |
| m.reset(); |
| this->addPath(src, m, mode); |
| } |
| |
| /** Add a copy of src to the path, transformed by matrix |
| @param src The path to add as a new contour |
| @param matrix Transform applied to src |
| @param mode Determines how path is added |
| */ |
| void addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode); |
| |
| /** |
| * Same as addPath(), but reverses the src input |
| */ |
| void reverseAddPath(const SkPath& src); |
| |
| /** Offset the path by (dx,dy), returning true on success |
| |
| @param dx The amount in the X direction to offset the entire path |
| @param dy The amount in the Y direction to offset the entire path |
| @param dst The translated path is written here |
| */ |
| void offset(SkScalar dx, SkScalar dy, SkPath* dst) const; |
| |
| /** Offset the path by (dx,dy), returning true on success |
| |
| @param dx The amount in the X direction to offset the entire path |
| @param dy The amount in the Y direction to offset the entire path |
| */ |
| void offset(SkScalar dx, SkScalar dy) { |
| this->offset(dx, dy, this); |
| } |
| |
| /** Transform the points in this path by matrix, and write the answer into |
| dst. |
| |
| @param matrix The matrix to apply to the path |
| @param dst The transformed path is written here |
| */ |
| void transform(const SkMatrix& matrix, SkPath* dst) const; |
| |
| /** Transform the points in this path by matrix |
| |
| @param matrix The matrix to apply to the path |
| */ |
| void transform(const SkMatrix& matrix) { |
| this->transform(matrix, this); |
| } |
| |
| /** Return the last point on the path. If no points have been added, (0,0) |
| is returned. If there are no points, this returns false, otherwise it |
| returns true. |
| |
| @param lastPt The last point on the path is returned here |
| */ |
| bool getLastPt(SkPoint* lastPt) const; |
| |
| /** Set the last point on the path. If no points have been added, |
| moveTo(x,y) is automatically called. |
| |
| @param x The new x-coordinate for the last point |
| @param y The new y-coordinate for the last point |
| */ |
| void setLastPt(SkScalar x, SkScalar y); |
| |
| /** Set the last point on the path. If no points have been added, moveTo(p) |
| is automatically called. |
| |
| @param p The new location for the last point |
| */ |
| void setLastPt(const SkPoint& p) { |
| this->setLastPt(p.fX, p.fY); |
| } |
| |
| enum SegmentMask { |
| kLine_SegmentMask = 1 << 0, |
| kQuad_SegmentMask = 1 << 1, |
| kConic_SegmentMask = 1 << 2, |
| kCubic_SegmentMask = 1 << 3, |
| }; |
| |
| /** |
| * Returns a mask, where each bit corresponding to a SegmentMask is |
| * set if the path contains 1 or more segments of that type. |
| * Returns 0 for an empty path (no segments). |
| */ |
| uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); } |
| |
| enum Verb { |
| kMove_Verb, //!< iter.next returns 1 point |
| kLine_Verb, //!< iter.next returns 2 points |
| kQuad_Verb, //!< iter.next returns 3 points |
| kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight() |
| kCubic_Verb, //!< iter.next returns 4 points |
| kClose_Verb, //!< iter.next returns 0 points |
| kDone_Verb, //!< iter.next returns 0 points |
| }; |
| |
| /** Iterate through all of the segments (lines, quadratics, cubics) of |
| each contours in a path. |
| |
| The iterator cleans up the segments along the way, removing degenerate |
| segments and adding close verbs where necessary. When the forceClose |
| argument is provided, each contour (as defined by a new starting |
| move command) will be completed with a close verb regardless of the |
| contour's contents. |
| */ |
| class SK_API Iter { |
| public: |
| Iter(); |
| Iter(const SkPath&, bool forceClose); |
| |
| void setPath(const SkPath&, bool forceClose); |
| |
| /** Return the next verb in this iteration of the path. When all |
| segments have been visited, return kDone_Verb. |
| |
| @param pts The points representing the current verb and/or segment |
| @param doConsumeDegerates If true, first scan for segments that are |
| deemed degenerate (too short) and skip those. |
| @param exact if doConsumeDegenerates is true and exact is true, skip only |
| degenerate elements with lengths exactly equal to zero. If exact |
| is false, skip degenerate elements with lengths close to zero. If |
| doConsumeDegenerates is false, exact has no effect. |
| @return The verb for the current segment |
| */ |
| Verb next(SkPoint pts[4], bool doConsumeDegerates = true, bool exact = false) { |
| if (doConsumeDegerates) { |
| this->consumeDegenerateSegments(exact); |
| } |
| return this->doNext(pts); |
| } |
| |
| /** |
| * Return the weight for the current conic. Only valid if the current |
| * segment return by next() was a conic. |
| */ |
| SkScalar conicWeight() const { return *fConicWeights; } |
| |
| /** If next() returns kLine_Verb, then this query returns true if the |
| line was the result of a close() command (i.e. the end point is the |
| initial moveto for this contour). If next() returned a different |
| verb, this returns an undefined value. |
| |
| @return If the last call to next() returned kLine_Verb, return true |
| if it was the result of an explicit close command. |
| */ |
| bool isCloseLine() const { return SkToBool(fCloseLine); } |
| |
| /** Returns true if the current contour is closed (has a kClose_Verb) |
| @return true if the current contour is closed (has a kClose_Verb) |
| */ |
| bool isClosedContour() const; |
| |
| private: |
| const SkPoint* fPts; |
| const uint8_t* fVerbs; |
| const uint8_t* fVerbStop; |
| const SkScalar* fConicWeights; |
| SkPoint fMoveTo; |
| SkPoint fLastPt; |
| SkBool8 fForceClose; |
| SkBool8 fNeedClose; |
| SkBool8 fCloseLine; |
| SkBool8 fSegmentState; |
| |
| inline const SkPoint& cons_moveTo(); |
| Verb autoClose(SkPoint pts[2]); |
| void consumeDegenerateSegments(bool exact); |
| Verb doNext(SkPoint pts[4]); |
| }; |
| |
| /** Iterate through the verbs in the path, providing the associated points. |
| */ |
| class SK_API RawIter { |
| public: |
| RawIter() {} |
| RawIter(const SkPath& path) { |
| setPath(path); |
| } |
| |
| void setPath(const SkPath& path) { |
| fRawIter.setPathRef(*path.fPathRef.get()); |
| } |
| |
| /** Return the next verb in this iteration of the path. When all |
| segments have been visited, return kDone_Verb. |
| |
| @param pts The points representing the current verb and/or segment |
| This must not be NULL. |
| @return The verb for the current segment |
| */ |
| Verb next(SkPoint pts[4]) { |
| return (Verb) fRawIter.next(pts); |
| } |
| |
| /** Return what the next verb will be, but do not visit the next segment. |
| |
| @return The verb for the next segment |
| */ |
| Verb peek() const { |
| return (Verb) fRawIter.peek(); |
| } |
| |
| SkScalar conicWeight() const { |
| return fRawIter.conicWeight(); |
| } |
| |
| private: |
| SkPathRef::Iter fRawIter; |
| friend class SkPath; |
| }; |
| |
| /** |
| * Returns true if the point { x, y } is contained by the path, taking into |
| * account the FillType. |
| */ |
| bool contains(SkScalar x, SkScalar y) const; |
| |
| void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const; |
| void dump() const; |
| void dumpHex() const; |
| |
| /** |
| * Write the path to the buffer, and return the number of bytes written. |
| * If buffer is NULL, it still returns the number of bytes. |
| */ |
| size_t writeToMemory(void* buffer) const; |
| /** |
| * Initializes the path from the buffer |
| * |
| * @param buffer Memory to read from |
| * @param length Amount of memory available in the buffer |
| * @return number of bytes read (must be a multiple of 4) or |
| * 0 if there was not enough memory available |
| */ |
| size_t readFromMemory(const void* buffer, size_t length); |
| |
| /** Returns a non-zero, globally unique value corresponding to the set of verbs |
| and points in the path (but not the fill type [except on Android skbug.com/1762]). |
| Each time the path is modified, a different generation ID will be returned. |
| */ |
| uint32_t getGenerationID() const; |
| |
| #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK |
| static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762) |
| #else |
| static const int kPathRefGenIDBitCnt = 32; |
| #endif |
| |
| SkDEBUGCODE(void validate() const;) |
| SkDEBUGCODE(void experimentalValidateRef() const { fPathRef->validate(); } ) |
| |
| private: |
| enum SerializationOffsets { |
| // 1 free bit at 29 |
| kUnused1_SerializationShift = 28, // 1 free bit |
| kDirection_SerializationShift = 26, // requires 2 bits |
| kIsVolatile_SerializationShift = 25, // requires 1 bit |
| // 1 free bit at 24 |
| kConvexity_SerializationShift = 16, // requires 8 bits |
| kFillType_SerializationShift = 8, // requires 8 bits |
| // low-8-bits are version |
| }; |
| |
| enum SerializationVersions { |
| kPathPrivFirstDirection_Version = 1, |
| kPathPrivLastMoveToIndex_Version = 2, |
| kCurrent_Version = 2 |
| }; |
| |
| sk_sp<SkPathRef> fPathRef; |
| int fLastMoveToIndex; |
| uint8_t fFillType; |
| mutable uint8_t fConvexity; |
| mutable SkAtomic<uint8_t, sk_memory_order_relaxed> fFirstDirection;// SkPathPriv::FirstDirection |
| SkBool8 fIsVolatile; |
| |
| /** Resets all fields other than fPathRef to their initial 'empty' values. |
| * Assumes the caller has already emptied fPathRef. |
| * On Android increments fGenerationID without reseting it. |
| */ |
| void resetFields(); |
| |
| /** Sets all fields other than fPathRef to the values in 'that'. |
| * Assumes the caller has already set fPathRef. |
| * Doesn't change fGenerationID or fSourcePath on Android. |
| */ |
| void copyFields(const SkPath& that); |
| |
| friend class Iter; |
| friend class SkPathPriv; |
| friend class SkPathStroker; |
| |
| /* Append, in reverse order, the first contour of path, ignoring path's |
| last point. If no moveTo() call has been made for this contour, the |
| first point is automatically set to (0,0). |
| */ |
| void reversePathTo(const SkPath&); |
| |
| // called before we add points for lineTo, quadTo, cubicTo, checking to see |
| // if we need to inject a leading moveTo first |
| // |
| // SkPath path; path.lineTo(...); <--- need a leading moveTo(0, 0) |
| // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo) |
| // |
| inline void injectMoveToIfNeeded(); |
| |
| inline bool hasOnlyMoveTos() const; |
| |
| Convexity internalGetConvexity() const; |
| |
| bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts, |
| bool* isClosed, Direction* direction) const; |
| |
| // called by stroker to see if all points (in the last contour) are equal and worthy of a cap |
| bool isZeroLengthSincePoint(int startPtIndex) const; |
| |
| /** Returns if the path can return a bound at no cost (true) or will have to |
| perform some computation (false). |
| */ |
| bool hasComputedBounds() const { |
| SkDEBUGCODE(this->validate();) |
| return fPathRef->hasComputedBounds(); |
| } |
| |
| |
| // 'rect' needs to be sorted |
| void setBounds(const SkRect& rect) { |
| SkPathRef::Editor ed(&fPathRef); |
| |
| ed.setBounds(rect); |
| } |
| |
| void setPt(int index, SkScalar x, SkScalar y); |
| |
| friend class SkAutoPathBoundsUpdate; |
| friend class SkAutoDisableOvalCheck; |
| friend class SkAutoDisableDirectionCheck; |
| friend class SkPathWriter; |
| friend class SkOpBuilder; |
| friend class SkBench_AddPathTest; // perf test reversePathTo |
| friend class PathTest_Private; // unit test reversePathTo |
| friend class ForceIsRRect_Private; // unit test isRRect |
| }; |
| |
| #endif |