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