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