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