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