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 /** Returns the number of points in the path. Up to max points are copied. 127 128 @param points If not null, receives up to max points 129 @param max The maximum number of points to copy into points 130 @return the actual number of points in the path 131 */ 132 int getPoints(SkPoint points[], int max) const; 133 134 //! Swap contents of this and other. Guaranteed not to throw 135 void swap(SkPath& other); 136 137 /** Returns the bounds of the path's points. If the path contains 0 or 1 138 points, the bounds is set to (0,0,0,0), and isEmpty() will return true. 139 Note: this bounds may be larger than the actual shape, since curves 140 do not extend as far as their control points. 141 */ getBounds()142 const SkRect& getBounds() const { 143 if (fBoundsIsDirty) { 144 this->computeBounds(); 145 } 146 return fBounds; 147 } 148 149 /** Calling this will, if the internal cache of the bounds is out of date, 150 update it so that subsequent calls to getBounds will be instanteous. 151 This also means that any copies or simple transformations of the path 152 will inherit the cached bounds. 153 */ updateBoundsCache()154 void updateBoundsCache() const { 155 // for now, just calling getBounds() is sufficient 156 this->getBounds(); 157 } 158 159 // Construction methods 160 161 /** Hint to the path to prepare for adding more points. This can allow the 162 path to more efficiently grow its storage. 163 164 @param extraPtCount The number of extra points the path should 165 preallocate for. 166 */ 167 void incReserve(unsigned extraPtCount); 168 169 /** Set the beginning of the next contour to the point (x,y). 170 171 @param x The x-coordinate of the start of a new contour 172 @param y The y-coordinate of the start of a new contour 173 */ 174 void moveTo(SkScalar x, SkScalar y); 175 176 /** Set the beginning of the next contour to the point 177 178 @param p The start of a new contour 179 */ moveTo(const SkPoint & p)180 void moveTo(const SkPoint& p) { 181 this->moveTo(p.fX, p.fY); 182 } 183 184 /** Set the beginning of the next contour relative to the last point on the 185 previous contour. If there is no previous contour, this is treated the 186 same as moveTo(). 187 188 @param dx The amount to add to the x-coordinate of the end of the 189 previous contour, to specify the start of a new contour 190 @param dy The amount to add to the y-coordinate of the end of the 191 previous contour, to specify the start of a new contour 192 */ 193 void rMoveTo(SkScalar dx, SkScalar dy); 194 195 /** Add a line from the last point to the specified point (x,y). If no 196 moveTo() call has been made for this contour, the first point is 197 automatically set to (0,0). 198 199 @param x The x-coordinate of the end of a line 200 @param y The y-coordinate of the end of a line 201 */ 202 void lineTo(SkScalar x, SkScalar y); 203 204 /** Add a line from the last point to the specified point. If no moveTo() 205 call has been made for this contour, the first point is automatically 206 set to (0,0). 207 208 @param p The end of a line 209 */ lineTo(const SkPoint & p)210 void lineTo(const SkPoint& p) { 211 this->lineTo(p.fX, p.fY); 212 } 213 214 /** Same as lineTo, but the coordinates are considered relative to the last 215 point on this contour. If there is no previous point, then a moveTo(0,0) 216 is inserted automatically. 217 218 @param dx The amount to add to the x-coordinate of the previous point 219 on this contour, to specify a line 220 @param dy The amount to add to the y-coordinate of the previous point 221 on this contour, to specify a line 222 */ 223 void rLineTo(SkScalar dx, SkScalar dy); 224 225 /** Add a quadratic bezier from the last point, approaching control point 226 (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for 227 this contour, the first point is automatically set to (0,0). 228 229 @param x1 The x-coordinate of the control point on a quadratic curve 230 @param y1 The y-coordinate of the control point on a quadratic curve 231 @param x2 The x-coordinate of the end point on a quadratic curve 232 @param y2 The y-coordinate of the end point on a quadratic curve 233 */ 234 void quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2); 235 236 /** Add a quadratic bezier from the last point, approaching control point 237 p1, and ending at p2. If no moveTo() call has been made for this 238 contour, the first point is automatically set to (0,0). 239 240 @param p1 The control point on a quadratic curve 241 @param p2 The end point on a quadratic curve 242 */ quadTo(const SkPoint & p1,const SkPoint & p2)243 void quadTo(const SkPoint& p1, const SkPoint& p2) { 244 this->quadTo(p1.fX, p1.fY, p2.fX, p2.fY); 245 } 246 247 /** Same as quadTo, but the coordinates are considered relative to the last 248 point on this contour. If there is no previous point, then a moveTo(0,0) 249 is inserted automatically. 250 251 @param dx1 The amount to add to the x-coordinate of the last point on 252 this contour, to specify the control point of a quadratic curve 253 @param dy1 The amount to add to the y-coordinate of the last point on 254 this contour, to specify the control point of a quadratic curve 255 @param dx2 The amount to add to the x-coordinate of the last point on 256 this contour, to specify the end point of a quadratic curve 257 @param dy2 The amount to add to the y-coordinate of the last point on 258 this contour, to specify the end point of a quadratic curve 259 */ 260 void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2); 261 262 /** Add a cubic bezier from the last point, approaching control points 263 (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been 264 made for this contour, the first point is automatically set to (0,0). 265 266 @param x1 The x-coordinate of the 1st control point on a cubic curve 267 @param y1 The y-coordinate of the 1st control point on a cubic curve 268 @param x2 The x-coordinate of the 2nd control point on a cubic curve 269 @param y2 The y-coordinate of the 2nd control point on a cubic curve 270 @param x3 The x-coordinate of the end point on a cubic curve 271 @param y3 The y-coordinate of the end point on a cubic curve 272 */ 273 void cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 274 SkScalar x3, SkScalar y3); 275 276 /** Add a cubic bezier from the last point, approaching control points p1 277 and p2, and ending at p3. If no moveTo() call has been made for this 278 contour, the first point is automatically set to (0,0). 279 280 @param p1 The 1st control point on a cubic curve 281 @param p2 The 2nd control point on a cubic curve 282 @param p3 The end point on a cubic curve 283 */ cubicTo(const SkPoint & p1,const SkPoint & p2,const SkPoint & p3)284 void cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) { 285 this->cubicTo(p1.fX, p1.fY, p2.fX, p2.fY, p3.fX, p3.fY); 286 } 287 288 /** Same as cubicTo, but the coordinates are considered relative to the 289 current point on this contour. If there is no previous point, then a 290 moveTo(0,0) is inserted automatically. 291 292 @param dx1 The amount to add to the x-coordinate of the last point on 293 this contour, to specify the 1st control point of a cubic curve 294 @param dy1 The amount to add to the y-coordinate of the last point on 295 this contour, to specify the 1st control point of a cubic curve 296 @param dx2 The amount to add to the x-coordinate of the last point on 297 this contour, to specify the 2nd control point of a cubic curve 298 @param dy2 The amount to add to the y-coordinate of the last point on 299 this contour, to specify the 2nd control point of a cubic curve 300 @param dx3 The amount to add to the x-coordinate of the last point on 301 this contour, to specify the end point of a cubic curve 302 @param dy3 The amount to add to the y-coordinate of the last point on 303 this contour, to specify the end point of a cubic curve 304 */ 305 void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 306 SkScalar x3, SkScalar y3); 307 308 /** Append the specified arc to the path as a new contour. If the start of 309 the path is different from the path's current last point, then an 310 automatic lineTo() is added to connect the current contour to the start 311 of the arc. However, if the path is empty, then we call moveTo() with 312 the first point of the arc. The sweep angle is treated mod 360. 313 314 @param oval The bounding oval defining the shape and size of the arc 315 @param startAngle Starting angle (in degrees) where the arc begins 316 @param sweepAngle Sweep angle (in degrees) measured clockwise. This is 317 treated mod 360. 318 @param forceMoveTo If true, always begin a new contour with the arc 319 */ 320 void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 321 bool forceMoveTo); 322 323 /** Append a line and arc to the current path. This is the same as the 324 PostScript call "arct". 325 */ 326 void arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 327 SkScalar radius); 328 329 /** Append a line and arc to the current path. This is the same as the 330 PostScript call "arct". 331 */ arcTo(const SkPoint p1,const SkPoint p2,SkScalar radius)332 void arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) { 333 this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); 334 } 335 336 /** Close the current contour. If the current point is not equal to the 337 first point of the contour, a line segment is automatically added. 338 */ 339 void close(); 340 341 enum Direction { 342 /** clockwise direction for adding closed contours */ 343 kCW_Direction, 344 /** counter-clockwise direction for adding closed contours */ 345 kCCW_Direction 346 }; 347 348 /** Add a closed rectangle contour to the path 349 @param rect The rectangle to add as a closed contour to the path 350 @param dir The direction to wind the rectangle's contour 351 */ 352 void addRect(const SkRect& rect, Direction dir = kCW_Direction); 353 354 /** Add a closed rectangle contour to the path 355 356 @param left The left side of a rectangle to add as a closed contour 357 to the path 358 @param top The top of a rectangle to add as a closed contour to the 359 path 360 @param right The right side of a rectangle to add as a closed contour 361 to the path 362 @param bottom The bottom of a rectangle to add as a closed contour to 363 the path 364 @param dir The direction to wind the rectangle's contour 365 */ 366 void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, 367 Direction dir = kCW_Direction); 368 369 /** Add a closed oval contour to the path 370 371 @param oval The bounding oval to add as a closed contour to the path 372 @param dir The direction to wind the oval's contour 373 */ 374 void addOval(const SkRect& oval, Direction dir = kCW_Direction); 375 376 /** Add a closed circle contour to the path 377 378 @param x The x-coordinate of the center of a circle to add as a 379 closed contour to the path 380 @param y The y-coordinate of the center of a circle to add as a 381 closed contour to the path 382 @param radius The radius of a circle to add as a closed contour to the 383 path 384 @param dir The direction to wind the circle's contour 385 */ 386 void addCircle(SkScalar x, SkScalar y, SkScalar radius, 387 Direction dir = kCW_Direction); 388 389 /** Add the specified arc to the path as a new contour. 390 391 @param oval The bounds of oval used to define the size of the arc 392 @param startAngle Starting angle (in degrees) where the arc begins 393 @param sweepAngle Sweep angle (in degrees) measured clockwise 394 */ 395 void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle); 396 397 /** Add a closed round-rectangle contour to the path 398 @param rect The bounds of a round-rectangle to add as a closed contour 399 @param rx The x-radius of the rounded corners on the round-rectangle 400 @param ry The y-radius of the rounded corners on the round-rectangle 401 @param dir The direction to wind the round-rectangle's contour 402 */ 403 void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 404 Direction dir = kCW_Direction); 405 406 /** Add a closed round-rectangle contour to the path. Each corner receives 407 two radius values [X, Y]. The corners are ordered top-left, top-right, 408 bottom-right, bottom-left. 409 @param rect The bounds of a round-rectangle to add as a closed contour 410 @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner 411 @param dir The direction to wind the round-rectangle's contour 412 */ 413 void addRoundRect(const SkRect& rect, const SkScalar radii[], 414 Direction dir = kCW_Direction); 415 416 /** Add a copy of src to the path, offset by (dx,dy) 417 @param src The path to add as a new contour 418 @param dx The amount to translate the path in X as it is added 419 @param dx The amount to translate the path in Y as it is added 420 */ 421 void addPath(const SkPath& src, SkScalar dx, SkScalar dy); 422 423 /** Add a copy of src to the path 424 */ addPath(const SkPath & src)425 void addPath(const SkPath& src) { 426 SkMatrix m; 427 m.reset(); 428 this->addPath(src, m); 429 } 430 431 /** Add a copy of src to the path, transformed by matrix 432 @param src The path to add as a new contour 433 */ 434 void addPath(const SkPath& src, const SkMatrix& matrix); 435 436 /** Offset the path by (dx,dy), returning true on success 437 438 @param dx The amount in the X direction to offset the entire path 439 @param dy The amount in the Y direction to offset the entire path 440 @param dst The translated path is written here 441 */ 442 void offset(SkScalar dx, SkScalar dy, SkPath* dst) const; 443 444 /** Offset the path by (dx,dy), returning true on success 445 446 @param dx The amount in the X direction to offset the entire path 447 @param dy The amount in the Y direction to offset the entire path 448 */ offset(SkScalar dx,SkScalar dy)449 void offset(SkScalar dx, SkScalar dy) { 450 this->offset(dx, dy, this); 451 } 452 453 /** Transform the points in this path by matrix, and write the answer into 454 dst. 455 456 @param matrix The matrix to apply to the path 457 @param dst The transformed path is written here 458 */ 459 void transform(const SkMatrix& matrix, SkPath* dst) const; 460 461 /** Transform the points in this path by matrix 462 463 @param matrix The matrix to apply to the path 464 */ transform(const SkMatrix & matrix)465 void transform(const SkMatrix& matrix) { 466 this->transform(matrix, this); 467 } 468 469 /** Return the last point on the path. If no points have been added, (0,0) 470 is returned. 471 472 @param lastPt The last point on the path is returned here 473 */ 474 void getLastPt(SkPoint* lastPt) const; 475 476 /** Set the last point on the path. If no points have been added, 477 moveTo(x,y) is automatically called. 478 479 @param x The new x-coordinate for the last point 480 @param y The new y-coordinate for the last point 481 */ 482 void setLastPt(SkScalar x, SkScalar y); 483 484 /** Set the last point on the path. If no points have been added, moveTo(p) 485 is automatically called. 486 487 @param p The new location for the last point 488 */ setLastPt(const SkPoint & p)489 void setLastPt(const SkPoint& p) { 490 this->setLastPt(p.fX, p.fY); 491 } 492 493 enum Verb { 494 kMove_Verb, //!< iter.next returns 1 point 495 kLine_Verb, //!< iter.next returns 2 points 496 kQuad_Verb, //!< iter.next returns 3 points 497 kCubic_Verb, //!< iter.next returns 4 points 498 kClose_Verb, //!< iter.next returns 1 point (the last point) 499 kDone_Verb //!< iter.next returns 0 points 500 }; 501 502 /** Iterate through all of the segments (lines, quadratics, cubics) of 503 each contours in a path. 504 */ 505 class Iter { 506 public: 507 Iter(); 508 Iter(const SkPath&, bool forceClose); 509 510 void setPath(const SkPath&, bool forceClose); 511 512 /** Return the next verb in this iteration of the path. When all 513 segments have been visited, return kDone_Verb. 514 515 @param pts The points representing the current verb and/or segment 516 @return The verb for the current segment 517 */ 518 Verb next(SkPoint pts[4]); 519 520 /** If next() returns kLine_Verb, then this query returns true if the 521 line was the result of a close() command (i.e. the end point is the 522 initial moveto for this contour). If next() returned a different 523 verb, this returns an undefined value. 524 525 @return If the last call to next() returned kLine_Verb, return true 526 if it was the result of an explicit close command. 527 */ isCloseLine()528 bool isCloseLine() const { return SkToBool(fCloseLine); } 529 530 /** Returns true if the current contour is closed (has a kClose_Verb) 531 @return true if the current contour is closed (has a kClose_Verb) 532 */ 533 bool isClosedContour() const; 534 535 private: 536 const SkPoint* fPts; 537 const uint8_t* fVerbs; 538 const uint8_t* fVerbStop; 539 SkPoint fMoveTo; 540 SkPoint fLastPt; 541 SkBool8 fForceClose; 542 SkBool8 fNeedClose; 543 SkBool8 fNeedMoveTo; 544 SkBool8 fCloseLine; 545 546 bool cons_moveTo(SkPoint pts[1]); 547 Verb autoClose(SkPoint pts[2]); 548 }; 549 550 #ifdef SK_DEBUG 551 /** @cond UNIT_TEST */ 552 void dump(bool forceClose, const char title[] = NULL) const; 553 /** @endcond */ 554 #endif 555 556 void flatten(SkFlattenableWriteBuffer&) const; 557 void unflatten(SkFlattenableReadBuffer&); 558 559 /** Subdivide the path so that no segment is longer that dist. 560 If bendLines is true, then turn all line segments into curves. 561 If dst == null, then the original path itself is modified (not const!) 562 */ 563 void subdivide(SkScalar dist, bool bendLines, SkPath* dst = NULL) const; 564 565 SkDEBUGCODE(void validate() const;) 566 567 private: 568 SkTDArray<SkPoint> fPts; 569 SkTDArray<uint8_t> fVerbs; 570 mutable SkRect fBounds; 571 mutable uint8_t fBoundsIsDirty; 572 uint8_t fFillType; 573 uint8_t fIsConvex; 574 575 // called, if dirty, by getBounds() 576 void computeBounds() const; 577 578 friend class Iter; 579 void cons_moveto(); 580 581 friend class SkPathStroker; 582 /* Append the first contour of path, ignoring path's initial point. If no 583 moveTo() call has been made for this contour, the first point is 584 automatically set to (0,0). 585 */ 586 void pathTo(const SkPath& path); 587 588 /* Append, in reverse order, the first contour of path, ignoring path's 589 last point. If no moveTo() call has been made for this contour, the 590 first point is automatically set to (0,0). 591 */ 592 void reversePathTo(const SkPath&); 593 594 friend const SkPoint* sk_get_path_points(const SkPath&, int index); 595 friend class SkAutoPathBoundsUpdate; 596 }; 597 598 #endif 599 600