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