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