1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SkPath_DEFINED 18 #define SkPath_DEFINED 19 20 #include "SkMatrix.h" 21 #include "SkTDArray.h" 22 23 class SkFlattenableReadBuffer; 24 class SkFlattenableWriteBuffer; 25 class SkAutoPathBoundsUpdate; 26 class SkString; 27 28 /** \class SkPath 29 30 The SkPath class encapsulates compound (multiple contour) geometric paths 31 consisting of straight line segments, quadratic curves, and cubic curves. 32 */ 33 class SkPath { 34 public: 35 SkPath(); 36 SkPath(const SkPath&); 37 ~SkPath(); 38 39 SkPath& operator=(const SkPath&); 40 41 friend bool operator==(const SkPath&, const SkPath&); 42 friend bool operator!=(const SkPath& a, const SkPath& b) { 43 return !(a == b); 44 } 45 46 enum FillType { 47 /** Specifies that "inside" is computed by a non-zero sum of signed 48 edge crossings 49 */ 50 kWinding_FillType, 51 /** Specifies that "inside" is computed by an odd number of edge 52 crossings 53 */ 54 kEvenOdd_FillType, 55 /** Same as Winding, but draws outside of the path, rather than inside 56 */ 57 kInverseWinding_FillType, 58 /** Same as EvenOdd, but draws outside of the path, rather than inside 59 */ 60 kInverseEvenOdd_FillType 61 }; 62 63 /** Return the path's fill type. This is used to define how "inside" is 64 computed. The default value is kWinding_FillType. 65 66 @return the path's fill type 67 */ getFillType()68 FillType getFillType() const { return (FillType)fFillType; } 69 70 /** Set the path's fill type. This is used to define how "inside" is 71 computed. The default value is kWinding_FillType. 72 73 @param ft The new fill type for this path 74 */ setFillType(FillType ft)75 void setFillType(FillType ft) { fFillType = SkToU8(ft); } 76 77 /** Returns true if the filltype is one of the Inverse variants */ isInverseFillType()78 bool isInverseFillType() const { return (fFillType & 2) != 0; } 79 80 /** Toggle between inverse and normal filltypes. This reverse the return 81 value of isInverseFillType() 82 */ toggleInverseFillType()83 void toggleInverseFillType() { fFillType ^= 2; } 84 85 /** Returns true if the path is flagged as being convex. This is not a 86 confirmed by any analysis, it is just the value set earlier. 87 */ isConvex()88 bool isConvex() const { return fIsConvex != 0; } 89 90 /** Set the isConvex flag to true or false. Convex paths may draw faster if 91 this flag is set, though setting this to true on a path that is in fact 92 not convex can give undefined results when drawn. Paths default to 93 isConvex == false 94 */ setIsConvex(bool isConvex)95 void setIsConvex(bool isConvex) { fIsConvex = (isConvex != 0); } 96 97 /** Clear any lines and curves from the path, making it empty. This frees up 98 internal storage associated with those segments. 99 This does NOT change the fill-type setting nor isConvex 100 */ 101 void reset(); 102 103 /** Similar to reset(), in that all lines and curves are removed from the 104 path. However, any internal storage for those lines/curves is retained, 105 making reuse of the path potentially faster. 106 This does NOT change the fill-type setting nor isConvex 107 */ 108 void rewind(); 109 110 /** Returns true if the path is empty (contains no lines or curves) 111 112 @return true if the path is empty (contains no lines or curves) 113 */ 114 bool isEmpty() const; 115 116 /** Returns true if the path specifies a rectangle. If so, and if rect is 117 not null, set rect to the bounds of the path. If the path does not 118 specify a rectangle, return false and ignore rect. 119 120 @param rect If not null, returns the bounds of the path if it specifies 121 a rectangle 122 @return true if the path specifies a rectangle 123 */ 124 bool isRect(SkRect* rect) const; 125 126 /** Return the number of points in the path 127 */ countPoints()128 int countPoints() const { 129 return this->getPoints(NULL, 0); 130 } 131 132 /** Return the point at the specified index. If the index is out of range 133 (i.e. is not 0 <= index < countPoints()) then the returned coordinates 134 will be (0,0) 135 */ 136 SkPoint getPoint(int index) const; 137 138 /** Returns the number of points in the path. Up to max points are copied. 139 140 @param points If not null, receives up to max points 141 @param max The maximum number of points to copy into points 142 @return the actual number of points in the path 143 */ 144 int getPoints(SkPoint points[], int max) const; 145 146 //! Swap contents of this and other. Guaranteed not to throw 147 void swap(SkPath& other); 148 149 /** Returns the bounds of the path's points. If the path contains 0 or 1 150 points, the bounds is set to (0,0,0,0), and isEmpty() will return true. 151 Note: this bounds may be larger than the actual shape, since curves 152 do not extend as far as their control points. 153 */ getBounds()154 const SkRect& getBounds() const { 155 if (fBoundsIsDirty) { 156 this->computeBounds(); 157 } 158 return fBounds; 159 } 160 161 /** Calling this will, if the internal cache of the bounds is out of date, 162 update it so that subsequent calls to getBounds will be instanteous. 163 This also means that any copies or simple transformations of the path 164 will inherit the cached bounds. 165 */ updateBoundsCache()166 void updateBoundsCache() const { 167 // for now, just calling getBounds() is sufficient 168 this->getBounds(); 169 } 170 171 // Construction methods 172 173 /** Hint to the path to prepare for adding more points. This can allow the 174 path to more efficiently grow its storage. 175 176 @param extraPtCount The number of extra points the path should 177 preallocate for. 178 */ 179 void incReserve(unsigned extraPtCount); 180 181 /** Set the beginning of the next contour to the point (x,y). 182 183 @param x The x-coordinate of the start of a new contour 184 @param y The y-coordinate of the start of a new contour 185 */ 186 void moveTo(SkScalar x, SkScalar y); 187 188 /** Set the beginning of the next contour to the point 189 190 @param p The start of a new contour 191 */ moveTo(const SkPoint & p)192 void moveTo(const SkPoint& p) { 193 this->moveTo(p.fX, p.fY); 194 } 195 196 /** Set the beginning of the next contour relative to the last point on the 197 previous contour. If there is no previous contour, this is treated the 198 same as moveTo(). 199 200 @param dx The amount to add to the x-coordinate of the end of the 201 previous contour, to specify the start of a new contour 202 @param dy The amount to add to the y-coordinate of the end of the 203 previous contour, to specify the start of a new contour 204 */ 205 void rMoveTo(SkScalar dx, SkScalar dy); 206 207 /** Add a line from the last point to the specified point (x,y). If no 208 moveTo() call has been made for this contour, the first point is 209 automatically set to (0,0). 210 211 @param x The x-coordinate of the end of a line 212 @param y The y-coordinate of the end of a line 213 */ 214 void lineTo(SkScalar x, SkScalar y); 215 216 /** Add a line from the last point to the specified point. If no moveTo() 217 call has been made for this contour, the first point is automatically 218 set to (0,0). 219 220 @param p The end of a line 221 */ lineTo(const SkPoint & p)222 void lineTo(const SkPoint& p) { 223 this->lineTo(p.fX, p.fY); 224 } 225 226 /** Same as lineTo, but the coordinates are considered relative to the last 227 point on this contour. If there is no previous point, then a moveTo(0,0) 228 is inserted automatically. 229 230 @param dx The amount to add to the x-coordinate of the previous point 231 on this contour, to specify a line 232 @param dy The amount to add to the y-coordinate of the previous point 233 on this contour, to specify a line 234 */ 235 void rLineTo(SkScalar dx, SkScalar dy); 236 237 /** Add a quadratic bezier from the last point, approaching control point 238 (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 239 this contour, the first point is automatically set to (0,0). 240 241 @param x1 The x-coordinate of the control point on a quadratic curve 242 @param y1 The y-coordinate of the control point on a quadratic curve 243 @param x2 The x-coordinate of the end point on a quadratic curve 244 @param y2 The y-coordinate of the end point on a quadratic curve 245 */ 246 void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); 247 248 /** Add a quadratic bezier from the last point, approaching control point 249 p1, and ending at p2. If no moveTo() call has been made for this 250 contour, the first point is automatically set to (0,0). 251 252 @param p1 The control point on a quadratic curve 253 @param p2 The end point on a quadratic curve 254 */ quadTo(const SkPoint & p1,const SkPoint & p2)255 void quadTo(const SkPoint& p1, const SkPoint& p2) { 256 this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); 257 } 258 259 /** Same as quadTo, but the coordinates are considered relative to the last 260 point on this contour. If there is no previous point, then a moveTo(0,0) 261 is inserted automatically. 262 263 @param dx1 The amount to add to the x-coordinate of the last point on 264 this contour, to specify the control point of a quadratic curve 265 @param dy1 The amount to add to the y-coordinate of the last point on 266 this contour, to specify the control point of a quadratic curve 267 @param dx2 The amount to add to the x-coordinate of the last point on 268 this contour, to specify the end point of a quadratic curve 269 @param dy2 The amount to add to the y-coordinate of the last point on 270 this contour, to specify the end point of a quadratic curve 271 */ 272 void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); 273 274 /** Add a cubic bezier from the last point, approaching control points 275 (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 276 made for this contour, the first point is automatically set to (0,0). 277 278 @param x1 The x-coordinate of the 1st control point on a cubic curve 279 @param y1 The y-coordinate of the 1st control point on a cubic curve 280 @param x2 The x-coordinate of the 2nd control point on a cubic curve 281 @param y2 The y-coordinate of the 2nd control point on a cubic curve 282 @param x3 The x-coordinate of the end point on a cubic curve 283 @param y3 The y-coordinate of the end point on a cubic curve 284 */ 285 void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 286 SkScalar x3, SkScalar y3); 287 288 /** Add a cubic bezier from the last point, approaching control points p1 289 and p2, and ending at p3. If no moveTo() call has been made for this 290 contour, the first point is automatically set to (0,0). 291 292 @param p1 The 1st control point on a cubic curve 293 @param p2 The 2nd control point on a cubic curve 294 @param p3 The end point on a cubic curve 295 */ cubicTo(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3)296 void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) { 297 this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); 298 } 299 300 /** Same as cubicTo, but the coordinates are considered relative to the 301 current point on this contour. If there is no previous point, then a 302 moveTo(0,0) is inserted automatically. 303 304 @param dx1 The amount to add to the x-coordinate of the last point on 305 this contour, to specify the 1st control point of a cubic curve 306 @param dy1 The amount to add to the y-coordinate of the last point on 307 this contour, to specify the 1st control point of a cubic curve 308 @param dx2 The amount to add to the x-coordinate of the last point on 309 this contour, to specify the 2nd control point of a cubic curve 310 @param dy2 The amount to add to the y-coordinate of the last point on 311 this contour, to specify the 2nd control point of a cubic curve 312 @param dx3 The amount to add to the x-coordinate of the last point on 313 this contour, to specify the end point of a cubic curve 314 @param dy3 The amount to add to the y-coordinate of the last point on 315 this contour, to specify the end point of a cubic curve 316 */ 317 void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 318 SkScalar x3, SkScalar y3); 319 320 /** Append the specified arc to the path as a new contour. If the start of 321 the path is different from the path's current last point, then an 322 automatic lineTo() is added to connect the current contour to the start 323 of the arc. However, if the path is empty, then we call moveTo() with 324 the first point of the arc. The sweep angle is treated mod 360. 325 326 @param oval The bounding oval defining the shape and size of the arc 327 @param startAngle Starting angle (in degrees) where the arc begins 328 @param sweepAngle Sweep angle (in degrees) measured clockwise. This is 329 treated mod 360. 330 @param forceMoveTo If true, always begin a new contour with the arc 331 */ 332 void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 333 bool forceMoveTo); 334 335 /** Append a line and arc to the current path. This is the same as the 336 PostScript call "arct". 337 */ 338 void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 339 SkScalar radius); 340 341 /** Append a line and arc to the current path. This is the same as the 342 PostScript call "arct". 343 */ arcTo(const SkPoint p1,const SkPoint p2,SkScalar radius)344 void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) { 345 this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); 346 } 347 348 /** Close the current contour. If the current point is not equal to the 349 first point of the contour, a line segment is automatically added. 350 */ 351 void close(); 352 353 enum Direction { 354 /** clockwise direction for adding closed contours */ 355 kCW_Direction, 356 /** counter-clockwise direction for adding closed contours */ 357 kCCW_Direction 358 }; 359 360 /** Add a closed rectangle contour to the path 361 @param rect The rectangle to add as a closed contour to the path 362 @param dir The direction to wind the rectangle's contour 363 */ 364 void addRect(const SkRect& rect, Direction dir = kCW_Direction); 365 366 /** Add a closed rectangle contour to the path 367 368 @param left The left side of a rectangle to add as a closed contour 369 to the path 370 @param top The top of a rectangle to add as a closed contour to the 371 path 372 @param right The right side of a rectangle to add as a closed contour 373 to the path 374 @param bottom The bottom of a rectangle to add as a closed contour to 375 the path 376 @param dir The direction to wind the rectangle's contour 377 */ 378 void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, 379 Direction dir = kCW_Direction); 380 381 /** Add a closed oval contour to the path 382 383 @param oval The bounding oval to add as a closed contour to the path 384 @param dir The direction to wind the oval's contour 385 */ 386 void addOval(const SkRect& oval, Direction dir = kCW_Direction); 387 388 /** Add a closed circle contour to the path 389 390 @param x The x-coordinate of the center of a circle to add as a 391 closed contour to the path 392 @param y The y-coordinate of the center of a circle to add as a 393 closed contour to the path 394 @param radius The radius of a circle to add as a closed contour to the 395 path 396 @param dir The direction to wind the circle's contour 397 */ 398 void addCircle(SkScalar x, SkScalar y, SkScalar radius, 399 Direction dir = kCW_Direction); 400 401 /** Add the specified arc to the path as a new contour. 402 403 @param oval The bounds of oval used to define the size of the arc 404 @param startAngle Starting angle (in degrees) where the arc begins 405 @param sweepAngle Sweep angle (in degrees) measured clockwise 406 */ 407 void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle); 408 409 /** Add a closed round-rectangle contour to the path 410 @param rect The bounds of a round-rectangle to add as a closed contour 411 @param rx The x-radius of the rounded corners on the round-rectangle 412 @param ry The y-radius of the rounded corners on the round-rectangle 413 @param dir The direction to wind the round-rectangle's contour 414 */ 415 void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 416 Direction dir = kCW_Direction); 417 418 /** Add a closed round-rectangle contour to the path. Each corner receives 419 two radius values [X, Y]. The corners are ordered top-left, top-right, 420 bottom-right, bottom-left. 421 @param rect The bounds of a round-rectangle to add as a closed contour 422 @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner 423 @param dir The direction to wind the round-rectangle's contour 424 */ 425 void addRoundRect(const SkRect& rect, const SkScalar radii[], 426 Direction dir = kCW_Direction); 427 428 /** Add a copy of src to the path, offset by (dx,dy) 429 @param src The path to add as a new contour 430 @param dx The amount to translate the path in X as it is added 431 @param dx The amount to translate the path in Y as it is added 432 */ 433 void addPath(const SkPath& src, SkScalar dx, SkScalar dy); 434 435 /** Add a copy of src to the path 436 */ addPath(const SkPath & src)437 void addPath(const SkPath& src) { 438 SkMatrix m; 439 m.reset(); 440 this->addPath(src, m); 441 } 442 443 /** Add a copy of src to the path, transformed by matrix 444 @param src The path to add as a new contour 445 */ 446 void addPath(const SkPath& src, const SkMatrix& matrix); 447 448 /** Offset the path by (dx,dy), returning true on success 449 450 @param dx The amount in the X direction to offset the entire path 451 @param dy The amount in the Y direction to offset the entire path 452 @param dst The translated path is written here 453 */ 454 void offset(SkScalar dx, SkScalar dy, SkPath* dst) const; 455 456 /** Offset the path by (dx,dy), returning true on success 457 458 @param dx The amount in the X direction to offset the entire path 459 @param dy The amount in the Y direction to offset the entire path 460 */ offset(SkScalar dx,SkScalar dy)461 void offset(SkScalar dx, SkScalar dy) { 462 this->offset(dx, dy, this); 463 } 464 465 /** Transform the points in this path by matrix, and write the answer into 466 dst. 467 468 @param matrix The matrix to apply to the path 469 @param dst The transformed path is written here 470 */ 471 void transform(const SkMatrix& matrix, SkPath* dst) const; 472 473 /** Transform the points in this path by matrix 474 475 @param matrix The matrix to apply to the path 476 */ transform(const SkMatrix & matrix)477 void transform(const SkMatrix& matrix) { 478 this->transform(matrix, this); 479 } 480 481 /** Return the last point on the path. If no points have been added, (0,0) 482 is returned. 483 484 @param lastPt The last point on the path is returned here 485 */ 486 void getLastPt(SkPoint* lastPt) const; 487 488 /** Set the last point on the path. If no points have been added, 489 moveTo(x,y) is automatically called. 490 491 @param x The new x-coordinate for the last point 492 @param y The new y-coordinate for the last point 493 */ 494 void setLastPt(SkScalar x, SkScalar y); 495 496 /** Set the last point on the path. If no points have been added, moveTo(p) 497 is automatically called. 498 499 @param p The new location for the last point 500 */ setLastPt(const SkPoint & p)501 void setLastPt(const SkPoint& p) { 502 this->setLastPt(p.fX, p.fY); 503 } 504 505 enum Verb { 506 kMove_Verb, //!< iter.next returns 1 point 507 kLine_Verb, //!< iter.next returns 2 points 508 kQuad_Verb, //!< iter.next returns 3 points 509 kCubic_Verb, //!< iter.next returns 4 points 510 kClose_Verb, //!< iter.next returns 1 point (the last point) 511 kDone_Verb //!< iter.next returns 0 points 512 }; 513 514 /** Iterate through all of the segments (lines, quadratics, cubics) of 515 each contours in a path. 516 */ 517 class Iter { 518 public: 519 Iter(); 520 Iter(const SkPath&, bool forceClose); 521 522 void setPath(const SkPath&, bool forceClose); 523 524 /** Return the next verb in this iteration of the path. When all 525 segments have been visited, return kDone_Verb. 526 527 @param pts The points representing the current verb and/or segment 528 @return The verb for the current segment 529 */ 530 Verb next(SkPoint pts[4]); 531 532 /** If next() returns kLine_Verb, then this query returns true if the 533 line was the result of a close() command (i.e. the end point is the 534 initial moveto for this contour). If next() returned a different 535 verb, this returns an undefined value. 536 537 @return If the last call to next() returned kLine_Verb, return true 538 if it was the result of an explicit close command. 539 */ isCloseLine()540 bool isCloseLine() const { return SkToBool(fCloseLine); } 541 542 /** Returns true if the current contour is closed (has a kClose_Verb) 543 @return true if the current contour is closed (has a kClose_Verb) 544 */ 545 bool isClosedContour() const; 546 547 private: 548 const SkPoint* fPts; 549 const uint8_t* fVerbs; 550 const uint8_t* fVerbStop; 551 SkPoint fMoveTo; 552 SkPoint fLastPt; 553 SkBool8 fForceClose; 554 SkBool8 fNeedClose; 555 SkBool8 fNeedMoveTo; 556 SkBool8 fCloseLine; 557 558 bool cons_moveTo(SkPoint pts[1]); 559 Verb autoClose(SkPoint pts[2]); 560 }; 561 562 void dump(bool forceClose, const char title[] = NULL) const; 563 void dump() const; 564 565 void flatten(SkFlattenableWriteBuffer&) const; 566 void unflatten(SkFlattenableReadBuffer&); 567 568 /** Subdivide the path so that no segment is longer that dist. 569 If bendLines is true, then turn all line segments into curves. 570 If dst == null, then the original path itself is modified (not const!) 571 */ 572 void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const; 573 574 SkDEBUGCODE(void validate() const;) 575 576 private: 577 SkTDArray<SkPoint> fPts; 578 SkTDArray<uint8_t> fVerbs; 579 mutable SkRect fBounds; 580 mutable uint8_t fBoundsIsDirty; 581 uint8_t fFillType; 582 uint8_t fIsConvex; 583 584 // called, if dirty, by getBounds() 585 void computeBounds() const; 586 587 friend class Iter; 588 void cons_moveto(); 589 590 friend class SkPathStroker; 591 /* Append the first contour of path, ignoring path's initial point. If no 592 moveTo() call has been made for this contour, the first point is 593 automatically set to (0,0). 594 */ 595 void pathTo(const SkPath& path); 596 597 /* Append, in reverse order, the first contour of path, ignoring path's 598 last point. If no moveTo() call has been made for this contour, the 599 first point is automatically set to (0,0). 600 */ 601 void reversePathTo(const SkPath&); 602 603 friend const SkPoint* sk_get_path_points(const SkPath&, int index); 604 friend class SkAutoPathBoundsUpdate; 605 }; 606 607 #endif 608 609