1#Topic Path 2#Alias Path_Reference ## 3#Alias Paths ## 4 5#Class SkPath 6 7#Code 8#Populate 9## 10 11Paths contain geometry. Paths may be empty, or contain one or more Verbs that 12outline a figure. Path always starts with a move verb to a Cartesian_Coordinate, 13and may be followed by additional verbs that add lines or curves. 14Adding a close verb makes the geometry into a continuous loop, a closed contour. 15Paths may contain any number of contours, each beginning with a move verb. 16 17Path contours may contain only a move verb, or may also contain lines, 18Quadratic_Beziers, Conics, and Cubic_Beziers. Path contours may be open or 19closed. 20 21When used to draw a filled area, Path describes whether the fill is inside or 22outside the geometry. Path also describes the winding rule used to fill 23overlapping contours. 24 25Internally, Path lazily computes metrics likes bounds and convexity. Call 26SkPath::updateBoundsCache to make Path thread safe. 27 28#Subtopic Verb 29#Alias Verbs ## 30#Line # line and curve type ## 31#Enum Verb 32#Line # controls how Path Points are interpreted ## 33 34#Code 35 enum Verb { 36 kMove_Verb, 37 kLine_Verb, 38 kQuad_Verb, 39 kConic_Verb, 40 kCubic_Verb, 41 kClose_Verb, 42 kDone_Verb, 43 }; 44## 45 46Verb instructs Path how to interpret one or more Point and optional Conic_Weight; 47manage Contour, and terminate Path. 48 49#Const kMove_Verb 0 50#Line # starts new Contour at next Point ## 51 Consecutive kMove_Verb are preserved but all but the last kMove_Verb is 52 ignored. kMove_Verb after other Verbs implicitly closes the previous Contour 53 if SkPaint::kFill_Style is set when drawn; otherwise, stroke is drawn open. 54 kMove_Verb as the last Verb is preserved but ignored. 55## 56#Const kLine_Verb 1 57#Line # adds Line from Last_Point to next Point ## 58 Line is a straight segment from Point to Point. Consecutive kLine_Verb 59 extend Contour. kLine_Verb at same position as prior kMove_Verb is 60 preserved, and draws Point if SkPaint::kStroke_Style is set, and 61 SkPaint::Cap is SkPaint::kSquare_Cap or SkPaint::kRound_Cap. kLine_Verb 62 at same position as prior line or curve Verb is preserved but is ignored. 63## 64#Const kQuad_Verb 2 65#Line # adds Quad from Last_Point ## 66 Adds Quad from Last_Point, using control Point, and end Point. 67 Quad is a parabolic section within tangents from Last_Point to control Point, 68 and control Point to end Point. 69## 70#Const kConic_Verb 3 71#Line # adds Conic from Last_Point ## 72 Adds Conic from Last_Point, using control Point, end Point, and Conic_Weight. 73 Conic is a elliptical, parabolic, or hyperbolic section within tangents 74 from Last_Point to control Point, and control Point to end Point, constrained 75 by Conic_Weight. Conic_Weight less than one is elliptical; equal to one is 76 parabolic (and identical to Quad); greater than one hyperbolic. 77## 78#Const kCubic_Verb 4 79#Line # adds Cubic from Last_Point ## 80 Adds Cubic from Last_Point, using two control Points, and end Point. 81 Cubic is a third-order Bezier_Curve section within tangents from Last_Point 82 to first control Point, and from second control Point to end Point. 83## 84#Const kClose_Verb 5 85#Line # closes Contour ## 86 Closes Contour, connecting Last_Point to kMove_Verb Point. Consecutive 87 kClose_Verb are preserved but only first has an effect. kClose_Verb after 88 kMove_Verb has no effect. 89## 90#Const kDone_Verb 6 91#Line # terminates Path ## 92 Not in Verb_Array, but returned by Path iterator. 93## 94 95Each Verb has zero or more Points stored in Path. 96Path iterator returns complete curve descriptions, duplicating shared Points 97for consecutive entries. 98 99#Table 100#Legend 101# Verb # Allocated Points # Iterated Points # Weights ## 102## 103# kMove_Verb # 1 # 1 # 0 ## 104# kLine_Verb # 1 # 2 # 0 ## 105# kQuad_Verb # 2 # 3 # 0 ## 106# kConic_Verb # 2 # 3 # 1 ## 107# kCubic_Verb # 3 # 4 # 0 ## 108# kClose_Verb # 0 # 1 # 0 ## 109# kDone_Verb # -- # 0 # 0 ## 110## 111 112#Example 113void draw(SkCanvas* canvas) { 114 SkPath path; 115 path.lineTo(20, 20); 116 path.quadTo(-10, -10, 30, 30); 117 path.close(); 118 path.cubicTo(1, 2, 3, 4, 5, 6); 119 path.conicTo(0, 0, 0, 0, 2); 120 uint8_t verbs[7]; 121 int count = path.getVerbs(verbs, (int) SK_ARRAY_COUNT(verbs)); 122 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close" }; 123 SkDebugf("verb count: %d\nverbs: ", count); 124 for (int i = 0; i < count; ++i) { 125 SkDebugf("k%s_Verb ", verbStr[verbs[i]]); 126 } 127 SkDebugf("\n"); 128} 129#StdOut 130verb count: 7 131verbs: kMove_Verb kLine_Verb kQuad_Verb kClose_Verb kMove_Verb kCubic_Verb kConic_Verb 132## 133## 134 135#Enum Verb ## 136#Subtopic Verb ## 137 138# ------------------------------------------------------------------------------ 139#Subtopic Direction 140#Line # contour orientation, clockwise or counterclockwise ## 141#Alias Directions ## 142 143#Enum Direction 144#Line # sets Contour clockwise or counterclockwise ## 145 146#Code 147 enum Direction : int { 148 kCW_Direction, 149 kCCW_Direction, 150 }; 151## 152 153Direction describes whether Contour is clockwise or counterclockwise. 154When Path contains multiple overlapping Contours, Direction together with 155Fill_Type determines whether overlaps are filled or form holes. 156 157Direction also determines how Contour is measured. For instance, dashing 158measures along Path to determine where to start and stop stroke; Direction 159will change dashed results as it steps clockwise or counterclockwise. 160 161Closed Contours like Rect, Round_Rect, Circle, and Oval added with 162kCW_Direction travel clockwise; the same added with kCCW_Direction 163travel counterclockwise. 164 165#Const kCW_Direction 0 166#Line # contour travels clockwise ## 167## 168#Const kCCW_Direction 1 169#Line # contour travels counterclockwise ## 170## 171 172 173#Example 174#Height 100 175void draw(SkCanvas* canvas) { 176 const SkPoint arrow[] = { {40, -5}, {45, 0}, {40, 5} }; 177 const SkRect rect = {10, 10, 90, 90}; 178 SkPaint rectPaint; 179 rectPaint.setAntiAlias(true); 180 SkPaint textPaint(rectPaint); 181 rectPaint.setStyle(SkPaint::kStroke_Style); 182 SkPaint arrowPaint(rectPaint); 183 SkPath arrowPath; 184 arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true); 185 arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 320, 0, 186 SkPath1DPathEffect::kRotate_Style)); 187 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { 188 canvas->drawRect(rect, rectPaint); 189 for (unsigned start : { 0, 1, 2, 3 } ) { 190 SkPath path; 191 path.addRect(rect, direction, start); 192 canvas->drawPath(path, arrowPaint); 193 } 194 canvas->drawString(SkPath::kCW_Direction == direction ? "CW" : "CCW", rect.centerX(), 195 rect.centerY(), textPaint); 196 canvas->translate(120, 0); 197 } 198} 199## 200 201#SeeAlso arcTo rArcTo isRect isNestedFillRects addRect addOval 202 203#Enum Direction ## 204#Subtopic Direction ## 205 206# ------------------------------------------------------------------------------ 207 208#Method SkPath() 209#In Constructors 210#Line # constructs with default values ## 211#Populate 212 213#Example 214 SkPath path; 215 SkDebugf("path is " "%s" "empty", path.isEmpty() ? "" : "not "); 216#StdOut 217path is empty 218## 219## 220 221#SeeAlso reset rewind 222 223## 224 225# ------------------------------------------------------------------------------ 226 227#Method SkPath(const SkPath& path) 228#In Constructors 229#Line # makes a shallow copy ## 230#Populate 231 232#Example 233#Description 234 Modifying one path does not effect another, even if they started as copies 235 of each other. 236## 237 SkPath path; 238 path.lineTo(20, 20); 239 SkPath path2(path); 240 path2.close(); 241 SkDebugf("path verbs: %d\n", path.countVerbs()); 242 SkDebugf("path2 verbs: %d\n", path2.countVerbs()); 243 path.reset(); 244 SkDebugf("after reset\n" "path verbs: %d\n", path.countVerbs()); 245 SkDebugf("path2 verbs: %d\n", path2.countVerbs()); 246#StdOut 247path verbs: 2 248path2 verbs: 3 249after reset 250path verbs: 0 251path2 verbs: 3 252## 253## 254 255#SeeAlso operator=(const SkPath& path) 256 257## 258 259# ------------------------------------------------------------------------------ 260 261#Method ~SkPath() 262 263#Line # decreases Reference_Count of owned objects ## 264#Populate 265 266#Example 267#Description 268delete calls Path destructor, but copy of original in path2 is unaffected. 269## 270void draw(SkCanvas* canvas) { 271 SkPath* path = new SkPath(); 272 path->lineTo(20, 20); 273 SkPath path2(*path); 274 delete path; 275 SkDebugf("path2 is " "%s" "empty", path2.isEmpty() ? "" : "not "); 276} 277## 278 279#SeeAlso SkPath() SkPath(const SkPath& path) operator=(const SkPath& path) 280 281## 282 283# ------------------------------------------------------------------------------ 284 285#Method SkPath& operator=(const SkPath& path) 286 287#Line # makes a shallow copy ## 288#Populate 289 290#Example 291SkPath path1; 292path1.addRect({10, 20, 30, 40}); 293SkPath path2 = path1; 294const SkRect& b1 = path1.getBounds(); 295SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom); 296const SkRect& b2 = path2.getBounds(); 297SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom); 298#StdOut 299path1 bounds = 10, 20, 30, 40 300path2 bounds = 10, 20, 30, 40 301#StdOut ## 302## 303 304#SeeAlso swap SkPath(const SkPath& path) 305 306## 307 308# ------------------------------------------------------------------------------ 309 310#Method bool operator==(const SkPath& a, const SkPath& b) 311 312#Line # compares Paths for equality ## 313#Populate 314 315#Example 316#Description 317rewind() removes Verb_Array but leaves storage; since storage is not compared, 318Path pair are equivalent. 319## 320void draw(SkCanvas* canvas) { 321 auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void { 322 SkDebugf("%s one %c= two\n", prefix, a == b ? '=' : '!'); 323 }; 324 SkPath one; 325 SkPath two; 326 debugster("empty", one, two); 327 one.moveTo(0, 0); 328 debugster("moveTo", one, two); 329 one.rewind(); 330 debugster("rewind", one, two); 331 one.moveTo(0, 0); 332 one.reset(); 333 debugster("reset", one, two); 334} 335#StdOut 336empty one == two 337moveTo one != two 338rewind one == two 339reset one == two 340## 341## 342 343#SeeAlso operator!=(const SkPath& a, const SkPath& b) operator=(const SkPath& path) 344 345## 346 347# ------------------------------------------------------------------------------ 348 349#Method bool operator!=(const SkPath& a, const SkPath& b) 350 351#Line # compares paths for inequality ## 352#Populate 353 354#Example 355#Description 356Path pair are equal though their convexity is not equal. 357## 358void draw(SkCanvas* canvas) { 359 auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void { 360 SkDebugf("%s one %c= two\n", prefix, a != b ? '!' : '='); 361 }; 362 SkPath one; 363 SkPath two; 364 debugster("empty", one, two); 365 one.addRect({10, 20, 30, 40}); 366 two.addRect({10, 20, 30, 40}); 367 debugster("add rect", one, two); 368 one.setConvexity(SkPath::kConcave_Convexity); 369 debugster("setConvexity", one, two); 370 SkDebugf("convexity %c=\n", one.getConvexity() == two.getConvexity() ? '=' : '!'); 371} 372#StdOut 373empty one == two 374add rect one == two 375setConvexity one == two 376convexity != 377## 378## 379 380## 381 382# ------------------------------------------------------------------------------ 383 384#Subtopic Property 385#Line # metrics and attributes ## 386## 387 388#Method bool isInterpolatable(const SkPath& compare) const 389#In Property 390#In Interpolate 391#Line # returns if pair contains equal counts of Verb_Array and Weights ## 392#Populate 393 394#Example 395 SkPath path, path2; 396 path.moveTo(20, 20); 397 path.lineTo(40, 40); 398 path.lineTo(20, 20); 399 path.lineTo(40, 40); 400 path.close(); 401 path2.addRect({20, 20, 40, 40}); 402 SkDebugf("paths are " "%s" "interpolatable", path.isInterpolatable(path2) ? "" : "not "); 403#StdOut 404paths are interpolatable 405## 406## 407 408#SeeAlso isInterpolatable 409 410## 411 412# ------------------------------------------------------------------------------ 413 414#Subtopic Interpolate 415#Line # weighted average of Path pair ## 416## 417 418#Method bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const 419#In Interpolate 420#Line # interpolates between Path pair ## 421Interpolates between Paths with Point_Array of equal size. 422Copy Verb_Array and Weights to out, and set out Point_Array to a weighted 423average of this Point_Array and ending Point_Array, using the formula: 424#Formula # (Path Point * weight) + ending Point * (1 - weight) ##. 425 426weight is most useful when between zero (ending Point_Array) and 427one (this Point_Array); will work with values outside of this 428range. 429 430interpolate() returns false and leaves out unchanged if Point_Array is not 431the same size as ending Point_Array. Call isInterpolatable to check Path 432compatibility prior to calling interpolate(). 433 434#Param ending Point_Array averaged with this Point_Array ## 435#Param weight contribution of this Point_Array, and 436 one minus contribution of ending Point_Array 437## 438#Param out Path replaced by interpolated averages ## 439 440#Return true if Paths contain same number of Points ## 441 442#Example 443#Height 60 444void draw(SkCanvas* canvas) { 445 SkPaint paint; 446 paint.setAntiAlias(true); 447 paint.setStyle(SkPaint::kStroke_Style); 448 SkPath path, path2; 449 path.moveTo(20, 20); 450 path.lineTo(40, 40); 451 path.lineTo(20, 40); 452 path.lineTo(40, 20); 453 path.close(); 454 path2.addRect({20, 20, 40, 40}); 455 for (SkScalar i = 0; i <= 1; i += 1.f / 6) { 456 SkPath interp; 457 path.interpolate(path2, i, &interp); 458 canvas->drawPath(interp, paint); 459 canvas->translate(30, 0); 460 } 461} 462## 463 464#SeeAlso isInterpolatable 465 466## 467 468# ------------------------------------------------------------------------------ 469#Subtopic Fill_Type 470#Line # fill rule, normal and inverted ## 471 472#Enum FillType 473#Line # sets winding rule and inverse fill ## 474 475#Code 476 enum FillType { 477 kWinding_FillType, 478 kEvenOdd_FillType, 479 kInverseWinding_FillType, 480 kInverseEvenOdd_FillType, 481 }; 482## 483 484Fill_Type selects the rule used to fill Path. Path set to kWinding_FillType 485fills if the sum of Contour edges is not zero, where clockwise edges add one, and 486counterclockwise edges subtract one. Path set to kEvenOdd_FillType fills if the 487number of Contour edges is odd. Each Fill_Type has an inverse variant that 488reverses the rule: 489kInverseWinding_FillType fills where the sum of Contour edges is zero; 490kInverseEvenOdd_FillType fills where the number of Contour edges is even. 491 492#Example 493#Height 100 494#Description 495The top row has two clockwise rectangles. The second row has one clockwise and 496one counterclockwise rectangle. The even-odd variants draw the same. The 497winding variants draw the top rectangle overlap, which has a winding of 2, the 498same as the outer parts of the top rectangles, which have a winding of 1. 499## 500void draw(SkCanvas* canvas) { 501 SkPath path; 502 path.addRect({10, 10, 30, 30}, SkPath::kCW_Direction); 503 path.addRect({20, 20, 40, 40}, SkPath::kCW_Direction); 504 path.addRect({10, 60, 30, 80}, SkPath::kCW_Direction); 505 path.addRect({20, 70, 40, 90}, SkPath::kCCW_Direction); 506 SkPaint strokePaint; 507 strokePaint.setStyle(SkPaint::kStroke_Style); 508 SkRect clipRect = {0, 0, 51, 100}; 509 canvas->drawPath(path, strokePaint); 510 SkPaint fillPaint; 511 for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType, 512 SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) { 513 canvas->translate(51, 0); 514 canvas->save(); 515 canvas->clipRect(clipRect); 516 path.setFillType(fillType); 517 canvas->drawPath(path, fillPaint); 518 canvas->restore(); 519 } 520} 521## 522 523#Const kWinding_FillType 0 524#Line # is enclosed by a non-zero sum of Contour Directions ## 525## 526#Const kEvenOdd_FillType 1 527#Line # is enclosed by an odd number of Contours ## 528## 529#Const kInverseWinding_FillType 2 530#Line # is enclosed by a zero sum of Contour Directions ## 531## 532#Const kInverseEvenOdd_FillType 3 533#Line # is enclosed by an even number of Contours ## 534## 535 536#Example 537#Height 230 538void draw(SkCanvas* canvas) { 539 SkPath path; 540 path.addRect({20, 10, 80, 70}, SkPath::kCW_Direction); 541 path.addRect({40, 30, 100, 90}, SkPath::kCW_Direction); 542 SkPaint strokePaint; 543 strokePaint.setStyle(SkPaint::kStroke_Style); 544 SkRect clipRect = {0, 0, 128, 128}; 545 canvas->drawPath(path, strokePaint); 546 canvas->drawLine({0, 50}, {120, 50}, strokePaint); 547 SkPaint textPaint; 548 textPaint.setAntiAlias(true); 549 SkScalar textHPos[] = { 10, 30, 60, 90, 110 }; 550 canvas->drawPosTextH("01210", 5, textHPos, 48, textPaint); 551 textPaint.setTextSize(18); 552 canvas->translate(0, 128); 553 canvas->scale(.5f, .5f); 554 canvas->drawString("inverse", 384, 150, textPaint); 555 SkPaint fillPaint; 556 for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType, 557 SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) { 558 canvas->save(); 559 canvas->clipRect(clipRect); 560 path.setFillType(fillType); 561 canvas->drawPath(path, fillPaint); 562 canvas->restore(); 563 canvas->drawString(fillType & 1 ? "even-odd" : "winding", 64, 170, textPaint); 564 canvas->translate(128, 0); 565 } 566} 567## 568 569#SeeAlso SkPaint::Style Direction getFillType setFillType 570 571## 572 573# ------------------------------------------------------------------------------ 574 575#Method FillType getFillType() const 576 577#In Fill_Type 578#Line # returns Fill_Type: winding, even-odd, inverse ## 579#Populate 580 581#Example 582 SkPath path; 583 SkDebugf("default path fill type is %s\n", 584 path.getFillType() == SkPath::kWinding_FillType ? "kWinding_FillType" : 585 path.getFillType() == SkPath::kEvenOdd_FillType ? "kEvenOdd_FillType" : 586 path.getFillType() == SkPath::kInverseWinding_FillType ? "kInverseWinding_FillType" : 587 "kInverseEvenOdd_FillType"); 588#StdOut 589default path fill type is kWinding_FillType 590## 591## 592 593#SeeAlso FillType setFillType isInverseFillType 594 595## 596 597# ------------------------------------------------------------------------------ 598 599#Method void setFillType(FillType ft) 600 601#In Fill_Type 602#Line # sets Fill_Type: winding, even-odd, inverse ## 603#Populate 604 605#Example 606#Description 607If empty Path is set to inverse FillType, it fills all pixels. 608## 609#Height 64 610 SkPath path; 611 path.setFillType(SkPath::kInverseWinding_FillType); 612 SkPaint paint; 613 paint.setColor(SK_ColorBLUE); 614 canvas->drawPath(path, paint); 615## 616 617#SeeAlso FillType getFillType toggleInverseFillType 618 619## 620 621# ------------------------------------------------------------------------------ 622 623#Method bool isInverseFillType() const 624 625#In Fill_Type 626#Line # returns if Fill_Type fills outside geometry ## 627#Populate 628 629#Example 630 SkPath path; 631 SkDebugf("default path fill type is inverse: %s\n", 632 path.isInverseFillType() ? "true" : "false"); 633#StdOut 634default path fill type is inverse: false 635## 636## 637 638#SeeAlso FillType getFillType setFillType toggleInverseFillType 639 640## 641 642# ------------------------------------------------------------------------------ 643 644#Method void toggleInverseFillType() 645 646#In Fill_Type 647#Line # toggles Fill_Type between inside and outside geometry ## 648Replaces FillType with its inverse. The inverse of FillType describes the area 649unmodified by the original FillType. 650 651#Table 652#Legend 653# FillType # toggled FillType ## 654## 655# kWinding_FillType # kInverseWinding_FillType ## 656# kEvenOdd_FillType # kInverseEvenOdd_FillType ## 657# kInverseWinding_FillType # kWinding_FillType ## 658# kInverseEvenOdd_FillType # kEvenOdd_FillType ## 659## 660 661#Example 662#Description 663Path drawn normally and through its inverse touches every pixel once. 664## 665#Height 100 666SkPath path; 667SkPaint paint; 668paint.setColor(SK_ColorRED); 669paint.setTextSize(80); 670paint.getTextPath("ABC", 3, 20, 80, &path); 671canvas->drawPath(path, paint); 672path.toggleInverseFillType(); 673paint.setColor(SK_ColorGREEN); 674canvas->drawPath(path, paint); 675## 676 677#SeeAlso FillType getFillType setFillType isInverseFillType 678 679## 680 681#Subtopic Fill_Type ## 682 683# ------------------------------------------------------------------------------ 684 685#Subtopic Convexity 686#Line # if Path is concave or convex ## 687 688#Enum Convexity 689#Line # returns if Path is convex or concave ## 690 691#Code 692 enum Convexity : uint8_t { 693 kUnknown_Convexity, 694 kConvex_Convexity, 695 kConcave_Convexity, 696 }; 697## 698 699Path is convex if it contains one Contour and Contour loops no more than 700360 degrees, and Contour angles all have same Direction. Convex Path 701may have better performance and require fewer resources on GPU_Surface. 702 703Path is concave when either at least one Direction change is clockwise and 704another is counterclockwise, or the sum of the changes in Direction is not 360 705degrees. 706 707Initially Path Convexity is kUnknown_Convexity. Path Convexity is computed 708if needed by destination Surface. 709 710#Const kUnknown_Convexity 0 711#Line # indicates Convexity has not been determined ## 712## 713#Const kConvex_Convexity 1 714#Line # one Contour made of a simple geometry without indentations ## 715## 716#Const kConcave_Convexity 2 717#Line # more than one Contour, or a geometry with indentations ## 718## 719 720#Example 721void draw(SkCanvas* canvas) { 722 SkPaint paint; 723 SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}}; 724 const char* labels[] = { "unknown", "convex", "concave" }; 725 for (SkScalar x : { 40, 100 } ) { 726 SkPath path; 727 quad[0].fX = x; 728 path.addPoly(quad, SK_ARRAY_COUNT(quad), true); 729 canvas->drawPath(path, paint); 730 canvas->drawString(labels[(int) path.getConvexity()], 30, 100, paint); 731 canvas->translate(100, 100); 732 } 733} 734## 735 736#SeeAlso Contour Direction getConvexity getConvexityOrUnknown setConvexity isConvex 737 738#Enum Convexity ## 739 740#Method Convexity getConvexity() const 741 742#In Convexity 743#Line # returns geometry convexity, computing if necessary ## 744#Populate 745 746#Example 747void draw(SkCanvas* canvas) { 748 auto debugster = [](const char* prefix, const SkPath& path) -> void { 749 SkDebugf("%s path convexity is %s\n", prefix, 750 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" : 751 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); }; 752 SkPath path; 753 debugster("initial", path); 754 path.lineTo(50, 0); 755 debugster("first line", path); 756 path.lineTo(50, 50); 757 debugster("second line", path); 758 path.lineTo(100, 50); 759 debugster("third line", path); 760} 761## 762 763#SeeAlso Convexity Contour Direction getConvexityOrUnknown setConvexity isConvex 764 765## 766 767# ------------------------------------------------------------------------------ 768 769#Method Convexity getConvexityOrUnknown() const 770 771#In Convexity 772#Line # returns geometry convexity if known ## 773#Populate 774 775#Example 776#Description 777Convexity is unknown unless getConvexity is called without a subsequent call 778that alters the path. 779## 780void draw(SkCanvas* canvas) { 781 auto debugster = [](const char* prefix, const SkPath& path) -> void { 782 SkDebugf("%s path convexity is %s\n", prefix, 783 SkPath::kUnknown_Convexity == path.getConvexityOrUnknown() ? "unknown" : 784 SkPath::kConvex_Convexity == path.getConvexityOrUnknown() ? "convex" : "concave"); }; 785 SkPath path; 786 debugster("initial", path); 787 path.lineTo(50, 0); 788 debugster("first line", path); 789 path.getConvexity(); 790 path.lineTo(50, 50); 791 debugster("second line", path); 792 path.lineTo(100, 50); 793 path.getConvexity(); 794 debugster("third line", path); 795} 796## 797 798#SeeAlso Convexity Contour Direction getConvexity setConvexity isConvex 799 800## 801 802# ------------------------------------------------------------------------------ 803 804#Method void setConvexity(Convexity convexity) 805 806#In Convexity 807#Line # sets if geometry is convex to avoid future computation ## 808#Populate 809 810#Example 811void draw(SkCanvas* canvas) { 812 auto debugster = [](const char* prefix, const SkPath& path) -> void { 813 SkDebugf("%s path convexity is %s\n", prefix, 814 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" : 815 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); }; 816 SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}}; 817 SkPath path; 818 path.addPoly(quad, SK_ARRAY_COUNT(quad), true); 819 debugster("initial", path); 820 path.setConvexity(SkPath::kConcave_Convexity); 821 debugster("after forcing concave", path); 822 path.setConvexity(SkPath::kUnknown_Convexity); 823 debugster("after forcing unknown", path); 824} 825## 826 827#SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown isConvex 828 829## 830 831# ------------------------------------------------------------------------------ 832 833#Method bool isConvex() const 834 835#In Convexity 836#Line # returns if geometry is convex ## 837#Populate 838 839#Example 840#Description 841Concave shape is erroneously considered convex after a forced call to 842setConvexity. 843## 844void draw(SkCanvas* canvas) { 845 SkPaint paint; 846 SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}}; 847 for (SkScalar x : { 40, 100 } ) { 848 SkPath path; 849 quad[0].fX = x; 850 path.addPoly(quad, SK_ARRAY_COUNT(quad), true); 851 path.setConvexity(SkPath::kConvex_Convexity); 852 canvas->drawPath(path, paint); 853 canvas->drawString(path.isConvex() ? "convex" : "not convex", 30, 100, paint); 854 canvas->translate(100, 100); 855 } 856} 857## 858 859#SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown setConvexity 860 861## 862 863#Subtopic Convexity ## 864 865# ------------------------------------------------------------------------------ 866 867#Method bool isOval(SkRect* bounds) const 868#In Property 869#Line # returns if describes Oval ## 870#Populate 871 872#Example 873void draw(SkCanvas* canvas) { 874 SkPaint paint; 875 SkPath path; 876 path.addOval({20, 20, 220, 220}); 877 SkRect bounds; 878 if (path.isOval(&bounds)) { 879 paint.setColor(0xFF9FBFFF); 880 canvas->drawRect(bounds, paint); 881 } 882 paint.setColor(0x3f000000); 883 canvas->drawPath(path, paint); 884} 885## 886 887#SeeAlso Oval addCircle addOval 888 889## 890 891# ------------------------------------------------------------------------------ 892 893#Method bool isRRect(SkRRect* rrect) const 894#In Property 895#Line # returns if describes Round_Rect ## 896#Populate 897 898#Example 899#Description 900Draw rounded rectangle and its bounds. 901## 902void draw(SkCanvas* canvas) { 903 SkPaint paint; 904 SkPath path; 905 path.addRRect(SkRRect::MakeRectXY({20, 20, 220, 220}, 30, 50)); 906 SkRRect rrect; 907 if (path.isRRect(&rrect)) { 908 const SkRect& bounds = rrect.rect(); 909 paint.setColor(0xFF9FBFFF); 910 canvas->drawRect(bounds, paint); 911 } 912 paint.setColor(0x3f000000); 913 canvas->drawPath(path, paint); 914} 915## 916 917#SeeAlso Round_Rect addRoundRect addRRect 918 919## 920 921# ------------------------------------------------------------------------------ 922 923#Method SkPath& reset() 924#In Constructors 925#Line # removes Verb_Array, Point_Array, and Weights; frees memory ## 926#Populate 927 928#Example 929 SkPath path1, path2; 930 path1.setFillType(SkPath::kInverseWinding_FillType); 931 path1.addRect({10, 20, 30, 40}); 932 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); 933 path1.reset(); 934 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); 935## 936 937#SeeAlso rewind() 938 939## 940 941# ------------------------------------------------------------------------------ 942 943#Method SkPath& rewind() 944#In Constructors 945#Line # removes Verb_Array, Point_Array, and Weights, keeping memory ## 946#Populate 947 948#Example 949#Description 950Although path1 retains its internal storage, it is indistinguishable from 951a newly initialized path. 952## 953 SkPath path1, path2; 954 path1.setFillType(SkPath::kInverseWinding_FillType); 955 path1.addRect({10, 20, 30, 40}); 956 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); 957 path1.rewind(); 958 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!'); 959## 960 961#SeeAlso reset() 962 963## 964 965# ------------------------------------------------------------------------------ 966 967#Method bool isEmpty() const 968#In Property 969#Line # returns if verb count is zero ## 970#Populate 971 972#Example 973void draw(SkCanvas* canvas) { 974 auto debugster = [](const char* prefix, const SkPath& path) -> void { 975 SkDebugf("%s path is %s" "empty\n", prefix, path.isEmpty() ? "" : "not "); 976 }; 977 SkPath path; 978 debugster("initial", path); 979 path.moveTo(0, 0); 980 debugster("after moveTo", path); 981 path.rewind(); 982 debugster("after rewind", path); 983 path.lineTo(0, 0); 984 debugster("after lineTo", path); 985 path.reset(); 986 debugster("after reset", path); 987} 988#StdOut 989initial path is empty 990after moveTo path is not empty 991after rewind path is empty 992after lineTo path is not empty 993after reset path is empty 994## 995## 996 997#SeeAlso SkPath() reset() rewind() 998 999## 1000 1001# ------------------------------------------------------------------------------ 1002 1003#Method bool isLastContourClosed() const 1004#In Property 1005#Line # returns if final Contour forms a loop ## 1006#Populate 1007 1008#Example 1009#Description 1010close() has no effect if Path is empty; isLastContourClosed() returns 1011false until Path has geometry followed by close(). 1012## 1013void draw(SkCanvas* canvas) { 1014 auto debugster = [](const char* prefix, const SkPath& path) -> void { 1015 SkDebugf("%s last contour is %s" "closed\n", prefix, 1016 path.isLastContourClosed() ? "" : "not "); 1017 }; 1018 SkPath path; 1019 debugster("initial", path); 1020 path.close(); 1021 debugster("after close", path); 1022 path.lineTo(0, 0); 1023 debugster("after lineTo", path); 1024 path.close(); 1025 debugster("after close", path); 1026} 1027#StdOut 1028initial last contour is not closed 1029after close last contour is not closed 1030after lineTo last contour is not closed 1031after close last contour is closed 1032## 1033## 1034 1035#SeeAlso close() 1036 1037## 1038 1039# ------------------------------------------------------------------------------ 1040 1041#Method bool isFinite() const 1042#In Property 1043#Line # returns if all Point values are finite ## 1044#Populate 1045 1046#Example 1047void draw(SkCanvas* canvas) { 1048 auto debugster = [](const char* prefix, const SkPath& path) -> void { 1049 SkDebugf("%s path is %s" "finite\n", prefix, path.isFinite() ? "" : "not "); 1050 }; 1051 SkPath path; 1052 debugster("initial", path); 1053 path.lineTo(SK_ScalarMax, SK_ScalarMax); 1054 debugster("after line", path); 1055 SkMatrix matrix; 1056 matrix.setScale(2, 2); 1057 path.transform(matrix); 1058 debugster("after scale", path); 1059} 1060#StdOut 1061initial path is finite 1062after line path is finite 1063after scale path is not finite 1064## 1065## 1066 1067#SeeAlso SkScalar 1068## 1069 1070# ------------------------------------------------------------------------------ 1071 1072#Method bool isVolatile() const 1073#In Property 1074#In Volatile 1075#Line # returns if Device should not cache ## 1076#Populate 1077 1078#Example 1079 SkPath path; 1080 SkDebugf("volatile by default is %s\n", path.isVolatile() ? "true" : "false"); 1081#StdOut 1082volatile by default is false 1083## 1084## 1085 1086#SeeAlso setIsVolatile 1087 1088## 1089 1090# ------------------------------------------------------------------------------ 1091#Subtopic Volatile 1092#Line # caching attribute ## 1093## 1094 1095#Method void setIsVolatile(bool isVolatile) 1096#In Volatile 1097#Line # sets if Device should not cache ## 1098#Populate 1099 1100#Example 1101#Height 50 1102#Width 50 1103 SkPaint paint; 1104 paint.setStyle(SkPaint::kStroke_Style); 1105 SkPath path; 1106 path.setIsVolatile(true); 1107 path.lineTo(40, 40); 1108 canvas->drawPath(path, paint); 1109 path.rewind(); 1110 path.moveTo(0, 40); 1111 path.lineTo(40, 0); 1112 canvas->drawPath(path, paint); 1113## 1114 1115#ToDo tie example to bench to show how volatile affects speed or dm to show resource usage ## 1116 1117#SeeAlso isVolatile 1118 1119## 1120 1121# ------------------------------------------------------------------------------ 1122 1123#Method static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) 1124#In Property 1125#Line # returns if Line is very small ## 1126#Populate 1127 1128#Example 1129#Description 1130As single precision floats, 100 and 100.000001 have the same bit representation, 1131and are exactly equal. 100 and 100.0001 have different bit representations, and 1132are not exactly equal, but are nearly equal. 1133## 1134void draw(SkCanvas* canvas) { 1135 SkPoint points[] = { {100, 100}, {100.000001f, 100.000001f}, {100.0001f, 100.0001f} }; 1136 for (size_t i = 0; i < SK_ARRAY_COUNT(points) - 1; ++i) { 1137 for (bool exact : { false, true } ) { 1138 SkDebugf("line from (%1.8g,%1.8g) to (%1.8g,%1.8g) is %s" "degenerate, %s\n", 1139 points[i].fX, points[i].fY, points[i + 1].fX, points[i + 1].fY, 1140 SkPath::IsLineDegenerate(points[i], points[i + 1], exact) 1141 ? "" : "not ", exact ? "exactly" : "nearly"); 1142 } 1143 } 1144} 1145#StdOut 1146line from (100,100) to (100,100) is degenerate, nearly 1147line from (100,100) to (100,100) is degenerate, exactly 1148line from (100,100) to (100.0001,100.0001) is degenerate, nearly 1149line from (100,100) to (100.0001,100.0001) is not degenerate, exactly 1150#StdOut ## 1151## 1152 1153#SeeAlso IsQuadDegenerate IsCubicDegenerate 1154## 1155 1156# ------------------------------------------------------------------------------ 1157 1158#Method static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, 1159 const SkPoint& p3, bool exact) 1160#In Property 1161#Line # returns if Quad is very small ## 1162#Populate 1163 1164#Example 1165#Description 1166As single precision floats: 100, 100.00001, and 100.00002 have different bit representations 1167but nearly the same value. Translating all three by 1000 gives them the same bit representation; 1168the fractional portion of the number can not be represented by the float and is lost. 1169## 1170void draw(SkCanvas* canvas) { 1171 auto debugster = [](const SkPath& path, bool exact) -> void { 1172 SkDebugf("quad (%1.8g,%1.8g), (%1.8g,%1.8g), (%1.8g,%1.8g) is %s" "degenerate, %s\n", 1173 path.getPoint(0).fX, path.getPoint(0).fY, path.getPoint(1).fX, 1174 path.getPoint(1).fY, path.getPoint(2).fX, path.getPoint(2).fY, 1175 SkPath::IsQuadDegenerate(path.getPoint(0), path.getPoint(1), path.getPoint(2), exact) ? 1176 "" : "not ", exact ? "exactly" : "nearly"); 1177 }; 1178 SkPath path, offset; 1179 path.moveTo({100, 100}); 1180 path.quadTo({100.00001f, 100.00001f}, {100.00002f, 100.00002f}); 1181 offset.addPath(path, 1000, 1000); 1182 for (bool exact : { false, true } ) { 1183 debugster(path, exact); 1184 debugster(offset, exact); 1185 } 1186} 1187#StdOut 1188quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is degenerate, nearly 1189quad (1100,1100), (1100,1100), (1100,1100) is degenerate, nearly 1190quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is not degenerate, exactly 1191quad (1100,1100), (1100,1100), (1100,1100) is degenerate, exactly 1192#StdOut ## 1193## 1194 1195#SeeAlso IsLineDegenerate IsCubicDegenerate 1196## 1197 1198# ------------------------------------------------------------------------------ 1199 1200#Method static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, 1201 const SkPoint& p3, const SkPoint& p4, bool exact) 1202#In Property 1203#Line # returns if Cubic is very small ## 1204#Populate 1205 1206#Example 1207void draw(SkCanvas* canvas) { 1208 SkPoint points[] = {{1, 0}, {0, 0}, {0, 0}, {0, 0}}; 1209 SkScalar step = 1; 1210 SkScalar prior, length = 0, degenerate = 0; 1211 do { 1212 prior = points[0].fX; 1213 step /= 2; 1214 if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3], false)) { 1215 degenerate = prior; 1216 points[0].fX += step; 1217 } else { 1218 length = prior; 1219 points[0].fX -= step; 1220 } 1221 } while (prior != points[0].fX); 1222 SkDebugf("%1.8g is degenerate\n", degenerate); 1223 SkDebugf("%1.8g is length\n", length); 1224} 1225#StdOut 12260.00024414062 is degenerate 12270.00024414065 is length 1228#StdOut ## 1229## 1230 1231## 1232 1233# ------------------------------------------------------------------------------ 1234 1235#Method bool isLine(SkPoint line[2]) const 1236#In Property 1237#Line # returns if describes Line ## 1238#Populate 1239 1240#Example 1241void draw(SkCanvas* canvas) { 1242 auto debugster = [](const char* prefix, const SkPath& path) -> void { 1243 SkPoint line[2]; 1244 if (path.isLine(line)) { 1245 SkDebugf("%s is line (%1.8g,%1.8g) (%1.8g,%1.8g)\n", prefix, 1246 line[0].fX, line[0].fY, line[1].fX, line[1].fY); 1247 } else { 1248 SkDebugf("%s is not line\n", prefix); 1249 } 1250 }; 1251 SkPath path; 1252 debugster("empty", path); 1253 path.lineTo(0, 0); 1254 debugster("zero line", path); 1255 path.rewind(); 1256 path.moveTo(10, 10); 1257 path.lineTo(20, 20); 1258 debugster("line", path); 1259 path.moveTo(20, 20); 1260 debugster("second move", path); 1261} 1262#StdOut 1263empty is not line 1264zero line is line (0,0) (0,0) 1265line is line (10,10) (20,20) 1266second move is not line 1267## 1268## 1269 1270## 1271 1272# ------------------------------------------------------------------------------ 1273 1274#Subtopic Point_Array 1275#Line # end points and control points for lines and curves ## 1276#Substitute SkPoint array 1277 1278Point_Array contains Points satisfying the allocated Points for 1279each Verb in Verb_Array. For instance, Path containing one Contour with Line 1280and Quad is described by Verb_Array: kMove_Verb, kLine_Verb, kQuad_Verb; and 1281one Point for move, one Point for Line, two Points for Quad; totaling four Points. 1282 1283Point_Array may be read directly from Path with getPoints, or inspected with 1284getPoint, with Iter, or with RawIter. 1285 1286#Method int getPoints(SkPoint points[], int max) const 1287 1288#In Point_Array 1289#Line # returns Point_Array ## 1290#Populate 1291 1292#Example 1293void draw(SkCanvas* canvas) { 1294 auto debugster = [](const char* prefix, const SkPath& path, SkPoint* points, int max) -> void { 1295 int count = path.getPoints(points, max); 1296 SkDebugf("%s point count: %d ", prefix, count); 1297 for (int i = 0; i < SkTMin(count, max) && points; ++i) { 1298 SkDebugf("(%1.8g,%1.8g) ", points[i].fX, points[i].fY); 1299 } 1300 SkDebugf("\n"); 1301 }; 1302 SkPath path; 1303 path.lineTo(20, 20); 1304 path.lineTo(-10, -10); 1305 SkPoint points[3]; 1306 debugster("no points", path, nullptr, 0); 1307 debugster("zero max", path, points, 0); 1308 debugster("too small", path, points, 2); 1309 debugster("just right", path, points, path.countPoints()); 1310} 1311#StdOut 1312no points point count: 3 1313zero max point count: 3 1314too small point count: 3 (0,0) (20,20) 1315just right point count: 3 (0,0) (20,20) (-10,-10) 1316## 1317## 1318 1319#SeeAlso countPoints getPoint 1320## 1321 1322#Method int countPoints() const 1323 1324#In Point_Array 1325#Line # returns Point_Array length ## 1326#Populate 1327 1328#Example 1329void draw(SkCanvas* canvas) { 1330 auto debugster = [](const char* prefix, const SkPath& path) -> void { 1331 SkDebugf("%s point count: %d\n", prefix, path.countPoints()); 1332 }; 1333 SkPath path; 1334 debugster("empty", path); 1335 path.lineTo(0, 0); 1336 debugster("zero line", path); 1337 path.rewind(); 1338 path.moveTo(10, 10); 1339 path.lineTo(20, 20); 1340 debugster("line", path); 1341 path.moveTo(20, 20); 1342 debugster("second move", path); 1343} 1344#StdOut 1345empty point count: 0 1346zero line point count: 2 1347line point count: 2 1348second move point count: 3 1349## 1350## 1351 1352#SeeAlso getPoints 1353## 1354 1355#Method SkPoint getPoint(int index) const 1356 1357#In Point_Array 1358#Line # returns entry from Point_Array ## 1359#Populate 1360 1361#Example 1362void draw(SkCanvas* canvas) { 1363 SkPath path; 1364 path.lineTo(20, 20); 1365 path.offset(-10, -10); 1366 for (int i= 0; i < path.countPoints(); ++i) { 1367 SkDebugf("point %d: (%1.8g,%1.8g)\n", i, path.getPoint(i).fX, path.getPoint(i).fY); 1368 } 1369} 1370#StdOut 1371point 0: (-10,-10) 1372point 1: (10,10) 1373## 1374## 1375 1376#SeeAlso countPoints getPoints 1377## 1378 1379 1380#Subtopic Point_Array ## 1381 1382# ------------------------------------------------------------------------------ 1383#Subtopic Verb_Array 1384#Line # line and curve type for points ## 1385 1386Verb_Array always starts with kMove_Verb. 1387If kClose_Verb is not the last entry, it is always followed by kMove_Verb; 1388the quantity of kMove_Verb equals the Contour count. 1389Verb_Array does not include or count kDone_Verb; it is a convenience 1390returned when iterating through Verb_Array. 1391 1392Verb_Array may be read directly from Path with getVerbs, or inspected with Iter, 1393or with RawIter. 1394 1395#Method int countVerbs() const 1396 1397#In Verb_Array 1398#Line # returns Verb_Array length ## 1399#Populate 1400 1401#Example 1402SkPath path; 1403SkDebugf("empty verb count: %d\n", path.countVerbs()); 1404path.addRoundRect({10, 20, 30, 40}, 5, 5); 1405SkDebugf("round rect verb count: %d\n", path.countVerbs()); 1406#StdOut 1407empty verb count: 0 1408round rect verb count: 10 1409## 1410## 1411 1412#SeeAlso getVerbs Iter RawIter 1413 1414## 1415 1416#Method int getVerbs(uint8_t verbs[], int max) const 1417 1418#In Verb_Array 1419#Line # returns Verb_Array ## 1420#Populate 1421 1422#Example 1423void draw(SkCanvas* canvas) { 1424 auto debugster = [](const char* prefix, const SkPath& path, uint8_t* verbs, int max) -> void { 1425 int count = path.getVerbs(verbs, max); 1426 SkDebugf("%s verb count: %d ", prefix, count); 1427 const char* verbStr[] = { "move", "line", "quad", "conic", "cubic", "close" }; 1428 for (int i = 0; i < SkTMin(count, max) && verbs; ++i) { 1429 SkDebugf("%s ", verbStr[verbs[i]]); 1430 } 1431 SkDebugf("\n"); 1432 }; 1433 SkPath path; 1434 path.lineTo(20, 20); 1435 path.lineTo(-10, -10); 1436 uint8_t verbs[3]; 1437 debugster("no verbs", path, nullptr, 0); 1438 debugster("zero max", path, verbs, 0); 1439 debugster("too small", path, verbs, 2); 1440 debugster("just right", path, verbs, path.countVerbs()); 1441} 1442#StdOut 1443no verbs verb count: 3 1444zero max verb count: 3 1445too small verb count: 3 move line 1446just right verb count: 3 move line line 1447## 1448## 1449 1450#SeeAlso countVerbs getPoints Iter RawIter 1451## 1452 1453#Subtopic Verb_Array ## 1454 1455# ------------------------------------------------------------------------------ 1456 1457#Method void swap(SkPath& other) 1458#In Operators 1459#Line # exchanges Path pair ## 1460#Populate 1461 1462#Example 1463SkPath path1, path2; 1464path1.addRect({10, 20, 30, 40}); 1465path1.swap(path2); 1466const SkRect& b1 = path1.getBounds(); 1467SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom); 1468const SkRect& b2 = path2.getBounds(); 1469SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom); 1470#StdOut 1471path1 bounds = 0, 0, 0, 0 1472path2 bounds = 10, 20, 30, 40 1473#StdOut ## 1474## 1475 1476#SeeAlso operator=(const SkPath& path) 1477 1478## 1479 1480# ------------------------------------------------------------------------------ 1481 1482#Method const SkRect& getBounds() const 1483#In Property 1484#Line # returns maximum and minimum of Point_Array ## 1485#Populate 1486 1487#Example 1488#Description 1489Bounds of upright Circle can be predicted from center and radius. 1490Bounds of rotated Circle includes control Points outside of filled area. 1491## 1492 auto debugster = [](const char* prefix, const SkPath& path) -> void { 1493 const SkRect& bounds = path.getBounds(); 1494 SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix, 1495 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); 1496 }; 1497 SkPath path; 1498 debugster("empty", path); 1499 path.addCircle(50, 45, 25); 1500 debugster("circle", path); 1501 SkMatrix matrix; 1502 matrix.setRotate(45, 50, 45); 1503 path.transform(matrix); 1504 debugster("rotated circle", path); 1505#StdOut 1506empty bounds = 0, 0, 0, 0 1507circle bounds = 25, 20, 75, 70 1508rotated circle bounds = 14.6447, 9.64466, 85.3553, 80.3553 1509## 1510## 1511 1512#SeeAlso computeTightBounds updateBoundsCache 1513 1514## 1515 1516# ------------------------------------------------------------------------------ 1517#Subtopic Utility 1518#Line # rarely called management functions ## 1519## 1520 1521#Method void updateBoundsCache() const 1522#In Utility 1523#Line # refreshes result of getBounds ## 1524#Populate 1525 1526#Example 1527 double times[2] = { 0, 0 }; 1528 for (int i = 0; i < 10000; ++i) { 1529 SkPath path; 1530 for (int j = 1; j < 100; ++ j) { 1531 path.addCircle(50 + j, 45 + j, 25 + j); 1532 } 1533 if (1 & i) { 1534 path.updateBoundsCache(); 1535 } 1536 double start = SkTime::GetNSecs(); 1537 (void) path.getBounds(); 1538 times[1 & i] += SkTime::GetNSecs() - start; 1539 } 1540 SkDebugf("uncached avg: %g ms\n", times[0] * 1e-6); 1541 SkDebugf("cached avg: %g ms\n", times[1] * 1e-6); 1542#StdOut 1543#Volatile 1544uncached avg: 0.18048 ms 1545cached avg: 0.182784 ms 1546## 1547## 1548 1549#SeeAlso getBounds 1550#ToDo the results don't make sense, need to profile to figure this out ## 1551 1552## 1553 1554# ------------------------------------------------------------------------------ 1555 1556#Method SkRect computeTightBounds() const 1557#In Property 1558#Line # returns extent of geometry ## 1559#Populate 1560 1561#Example 1562 auto debugster = [](const char* prefix, const SkPath& path) -> void { 1563 const SkRect& bounds = path.computeTightBounds(); 1564 SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix, 1565 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); 1566 }; 1567 SkPath path; 1568 debugster("empty", path); 1569 path.addCircle(50, 45, 25); 1570 debugster("circle", path); 1571 SkMatrix matrix; 1572 matrix.setRotate(45, 50, 45); 1573 path.transform(matrix); 1574 debugster("rotated circle", path); 1575#StdOut 1576empty bounds = 0, 0, 0, 0 1577circle bounds = 25, 20, 75, 70 1578rotated circle bounds = 25, 20, 75, 70 1579## 1580## 1581 1582#SeeAlso getBounds 1583 1584## 1585 1586# ------------------------------------------------------------------------------ 1587 1588#Method bool conservativelyContainsRect(const SkRect& rect) const 1589#In Property 1590#Line # returns true if Rect may be inside ## 1591#Populate 1592 1593#Example 1594#Height 140 1595#Description 1596Rect is drawn in blue if it is contained by red Path. 1597## 1598void draw(SkCanvas* canvas) { 1599 SkPath path; 1600 path.addRoundRect({10, 20, 54, 120}, 10, 20); 1601 SkRect tests[] = { 1602 { 10, 40, 54, 80 }, 1603 { 25, 20, 39, 120 }, 1604 { 15, 25, 49, 115 }, 1605 { 13, 27, 51, 113 }, 1606 }; 1607 for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) { 1608 SkPaint paint; 1609 paint.setColor(SK_ColorRED); 1610 canvas->drawPath(path, paint); 1611 bool rectInPath = path.conservativelyContainsRect(tests[i]); 1612 paint.setColor(rectInPath ? SK_ColorBLUE : SK_ColorBLACK); 1613 canvas->drawRect(tests[i], paint); 1614 canvas->translate(64, 0); 1615 } 1616} 1617## 1618 1619#SeeAlso contains Op Rect Convexity 1620 1621## 1622 1623# ------------------------------------------------------------------------------ 1624 1625#Method void incReserve(int extraPtCount) 1626#In Utility 1627#Line # reserves space for additional data ## 1628#Populate 1629 1630#Example 1631#Height 192 1632void draw(SkCanvas* canvas) { 1633 auto addPoly = [](int sides, SkScalar size, SkPath* path) -> void { 1634 path->moveTo(size, 0); 1635 for (int i = 1; i < sides; i++) { 1636 SkScalar c, s = SkScalarSinCos(SK_ScalarPI * 2 * i / sides, &c); 1637 path->lineTo(c * size, s * size); 1638 } 1639 path->close(); 1640 }; 1641 SkPath path; 1642 path.incReserve(3 + 4 + 5 + 6 + 7 + 8 + 9); 1643 for (int sides = 3; sides < 10; ++sides) { 1644 addPoly(sides, sides, &path); 1645 } 1646 SkMatrix matrix; 1647 matrix.setScale(10, 10, -10, -10); 1648 path.transform(matrix); 1649 SkPaint paint; 1650 paint.setStyle(SkPaint::kStroke_Style); 1651 canvas->drawPath(path, paint); 1652} 1653## 1654 1655#SeeAlso Point_Array 1656 1657## 1658 1659#Method void shrinkToFit() 1660#In Utility 1661#Line # removes unused reserved space ## 1662#Populate 1663 1664#NoExample 1665#Height 192 1666void draw(SkCanvas* canvas) { 1667 SkPath skinnyPath; 1668 skinnyPath.lineTo(100, 100); 1669 skinnyPath.shrinkToFit(); 1670 1671 SkDebugf("no wasted Path capacity in my house!\n"); 1672} 1673## 1674 1675#SeeAlso incReserve 1676 1677## 1678 1679# ------------------------------------------------------------------------------ 1680#Subtopic Build 1681#Line # adds points and verbs to path ## 1682## 1683 1684#Method SkPath& moveTo(SkScalar x, SkScalar y) 1685#In Build 1686#Line # starts Contour ## 1687#Populate 1688 1689#Example 1690 #Width 140 1691 #Height 100 1692 void draw(SkCanvas* canvas) { 1693 SkRect rect = { 20, 20, 120, 80 }; 1694 SkPath path; 1695 path.addRect(rect); 1696 path.moveTo(rect.fLeft, rect.fTop); 1697 path.lineTo(rect.fRight, rect.fBottom); 1698 path.moveTo(rect.fLeft, rect.fBottom); 1699 path.lineTo(rect.fRight, rect.fTop); 1700 SkPaint paint; 1701 paint.setStyle(SkPaint::kStroke_Style); 1702 canvas->drawPath(path, paint); 1703 } 1704## 1705 1706#SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close() 1707 1708## 1709 1710#Method SkPath& moveTo(const SkPoint& p) 1711#Populate 1712 1713#Example 1714 #Width 128 1715 #Height 128 1716void draw(SkCanvas* canvas) { 1717 SkPoint data[][3] = {{{30,40},{60,60},{90,30}}, {{30,120},{60,100},{90,120}}, 1718 {{60,100},{60,40},{70,30}}, {{60,40},{50,20},{70,30}}}; 1719 SkPath path; 1720 for (unsigned i = 0; i < SK_ARRAY_COUNT(data); ++i) { 1721 path.moveTo(data[i][0]); 1722 path.lineTo(data[i][1]); 1723 path.lineTo(data[i][2]); 1724 } 1725 SkPaint paint; 1726 paint.setStyle(SkPaint::kStroke_Style); 1727 canvas->drawPath(path, paint); 1728} 1729## 1730 1731#SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close() 1732 1733## 1734 1735#Method SkPath& rMoveTo(SkScalar dx, SkScalar dy) 1736#In Build 1737#Line # starts Contour relative to Last_Point ## 1738#Populate 1739 1740#Example 1741 #Height 100 1742 SkPath path; 1743 path.addRect({20, 20, 80, 80}, SkPath::kCW_Direction, 2); 1744 path.rMoveTo(25, 2); 1745 SkVector arrow[] = {{0, -4}, {-20, 0}, {0, -3}, {-5, 5}, {5, 5}, {0, -3}, {20, 0}}; 1746 for (unsigned i = 0; i < SK_ARRAY_COUNT(arrow); ++i) { 1747 path.rLineTo(arrow[i].fX, arrow[i].fY); 1748 } 1749 SkPaint paint; 1750 canvas->drawPath(path, paint); 1751 SkPoint lastPt; 1752 path.getLastPt(&lastPt); 1753 canvas->drawString("start", lastPt.fX, lastPt.fY, paint); 1754## 1755 1756#SeeAlso Contour lineTo moveTo quadTo conicTo cubicTo close() 1757 1758## 1759 1760# ------------------------------------------------------------------------------ 1761 1762#Method SkPath& lineTo(SkScalar x, SkScalar y) 1763#In Build 1764#Line # appends Line ## 1765#Populate 1766 1767#Example 1768#Height 100 1769###$ 1770void draw(SkCanvas* canvas) { 1771 SkPaint paint; 1772 paint.setAntiAlias(true); 1773 paint.setTextSize(72); 1774 canvas->drawString("#", 120, 80, paint); 1775 paint.setStyle(SkPaint::kStroke_Style); 1776 paint.setStrokeWidth(5); 1777 SkPath path; 1778 SkPoint hash[] = {{58, 28}, {43, 80}, {37, 45}, {85, 45}}; 1779 SkVector offsets[] = {{0, 0}, {17, 0}, {0, 0}, {-5, 17}}; 1780 unsigned o = 0; 1781 for (unsigned i = 0; i < SK_ARRAY_COUNT(hash); i += 2) { 1782 for (unsigned j = 0; j < 2; o++, j++) { 1783 path.moveTo(hash[i].fX + offsets[o].fX, hash[i].fY + offsets[o].fY); 1784 path.lineTo(hash[i + 1].fX + offsets[o].fX, hash[i + 1].fY + offsets[o].fY); 1785 } 1786 } 1787 canvas->drawPath(path, paint); 1788} 1789$$$# 1790## 1791 1792#SeeAlso Contour moveTo rLineTo addRect 1793 1794## 1795 1796# ------------------------------------------------------------------------------ 1797 1798#Method SkPath& lineTo(const SkPoint& p) 1799#Populate 1800 1801#Example 1802#Height 100 1803 SkPath path; 1804 SkVector oxo[] = {{25, 25}, {35, 35}, {25, 35}, {35, 25}, 1805 {40, 20}, {40, 80}, {60, 20}, {60, 80}, 1806 {20, 40}, {80, 40}, {20, 60}, {80, 60}}; 1807 for (unsigned i = 0; i < SK_ARRAY_COUNT(oxo); i += 2) { 1808 path.moveTo(oxo[i]); 1809 path.lineTo(oxo[i + 1]); 1810 } 1811 SkPaint paint; 1812 paint.setStyle(SkPaint::kStroke_Style); 1813 canvas->drawPath(path, paint); 1814## 1815 1816#SeeAlso Contour moveTo rLineTo addRect 1817 1818## 1819 1820# ------------------------------------------------------------------------------ 1821 1822#Method SkPath& rLineTo(SkScalar dx, SkScalar dy) 1823#In Build 1824#Line # appends Line relative to Last_Point ## 1825#Populate 1826 1827#Example 1828#Height 128 1829void draw(SkCanvas* canvas) { 1830 SkPaint paint; 1831 paint.setAntiAlias(true); 1832 paint.setStyle(SkPaint::kStroke_Style); 1833 SkPath path; 1834 path.moveTo(10, 98); 1835 SkScalar x = 0, y = 0; 1836 for (int i = 10; i < 100; i += 5) { 1837 x += i * ((i & 2) - 1); 1838 y += i * (((i + 1) & 2) - 1); 1839 path.rLineTo(x, y); 1840 1841 } 1842 canvas->drawPath(path, paint); 1843} 1844## 1845 1846#SeeAlso Contour moveTo lineTo addRect 1847 1848## 1849 1850# ------------------------------------------------------------------------------ 1851#Subtopic Quad 1852#Alias Quad ## 1853#Alias Quads ## 1854#Alias Quadratic_Bezier ## 1855#Alias Quadratic_Beziers ## 1856#Line # curve described by second-order polynomial ## 1857 1858Quad describes a Quadratic_Bezier, a second-order curve identical to a section 1859of a parabola. Quad begins at a start Point, curves towards a control Point, 1860and then curves to an end Point. 1861 1862#Example 1863#Height 110 1864void draw(SkCanvas* canvas) { 1865 SkPaint paint; 1866 paint.setAntiAlias(true); 1867 paint.setStyle(SkPaint::kStroke_Style); 1868 SkPoint quadPts[] = {{20, 90}, {120, 10}, {220, 90}}; 1869 canvas->drawLine(quadPts[0], quadPts[1], paint); 1870 canvas->drawLine(quadPts[1], quadPts[2], paint); 1871 SkPath path; 1872 path.moveTo(quadPts[0]); 1873 path.quadTo(quadPts[1], quadPts[2]); 1874 paint.setStrokeWidth(3); 1875 canvas->drawPath(path, paint); 1876} 1877## 1878 1879Quad is a special case of Conic where Conic_Weight is set to one. 1880 1881Quad is always contained by the triangle connecting its three Points. Quad 1882begins tangent to the line between start Point and control Point, and ends 1883tangent to the line between control Point and end Point. 1884 1885#Example 1886#Height 160 1887void draw(SkCanvas* canvas) { 1888 SkPaint paint; 1889 paint.setAntiAlias(true); 1890 paint.setStyle(SkPaint::kStroke_Style); 1891 SkPoint quadPts[] = {{20, 150}, {120, 10}, {220, 150}}; 1892 SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 }; 1893 for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) { 1894 paint.setColor(0x7fffffff & colors[i]); 1895 paint.setStrokeWidth(1); 1896 canvas->drawLine(quadPts[0], quadPts[1], paint); 1897 canvas->drawLine(quadPts[1], quadPts[2], paint); 1898 SkPath path; 1899 path.moveTo(quadPts[0]); 1900 path.quadTo(quadPts[1], quadPts[2]); 1901 paint.setStrokeWidth(3); 1902 paint.setColor(colors[i]); 1903 canvas->drawPath(path, paint); 1904 quadPts[1].fY += 30; 1905 } 1906} 1907## 1908 1909#Method SkPath& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) 1910 1911#In Quad 1912#Line # appends Quad ## 1913#Populate 1914 1915#Example 1916 void draw(SkCanvas* canvas) { 1917 SkPaint paint; 1918 paint.setAntiAlias(true); 1919 paint.setStyle(SkPaint::kStroke_Style); 1920 SkPath path; 1921 path.moveTo(0, -10); 1922 for (int i = 0; i < 128; i += 16) { 1923 path.quadTo( 10 + i, -10 - i, 10 + i, 0); 1924 path.quadTo( 14 + i, 14 + i, 0, 14 + i); 1925 path.quadTo(-18 - i, 18 + i, -18 - i, 0); 1926 path.quadTo(-22 - i, -22 - i, 0, -22 - i); 1927 } 1928 path.offset(128, 128); 1929 canvas->drawPath(path, paint); 1930 } 1931 ## 1932 1933 #SeeAlso Contour moveTo conicTo rQuadTo 1934 1935## 1936 1937#Method SkPath& quadTo(const SkPoint& p1, const SkPoint& p2) 1938#In Build 1939#In Quad 1940#Populate 1941 1942#Example 1943 void draw(SkCanvas* canvas) { 1944 SkPaint paint; 1945 paint.setStyle(SkPaint::kStroke_Style); 1946 paint.setAntiAlias(true); 1947 SkPath path; 1948 SkPoint pts[] = {{128, 10}, {10, 214}, {236, 214}}; 1949 path.moveTo(pts[1]); 1950 for (int i = 0; i < 3; ++i) { 1951 path.quadTo(pts[i % 3], pts[(i + 2) % 3]); 1952 } 1953 canvas->drawPath(path, paint); 1954 } 1955 ## 1956 1957 #SeeAlso Contour moveTo conicTo rQuadTo 1958 1959## 1960 1961#Method SkPath& rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2) 1962#In Build 1963#In Quad 1964#Line # appends Quad relative to Last_Point ## 1965#Populate 1966 1967#Example 1968 void draw(SkCanvas* canvas) { 1969 SkPaint paint; 1970 paint.setAntiAlias(true); 1971 SkPath path; 1972 path.moveTo(128, 20); 1973 path.rQuadTo(-6, 10, -7, 10); 1974 for (int i = 1; i < 32; i += 4) { 1975 path.rQuadTo(10 + i, 10 + i, 10 + i * 4, 10); 1976 path.rQuadTo(-10 - i, 10 + i, -10 - (i + 2) * 4, 10); 1977 } 1978 path.quadTo(92, 220, 128, 215); 1979 canvas->drawPath(path, paint); 1980 } 1981 ## 1982 1983 #SeeAlso Contour moveTo conicTo quadTo 1984 1985## 1986 1987#Subtopic Quad ## 1988 1989# ------------------------------------------------------------------------------ 1990 1991#Subtopic Conic 1992#Alias Conic ## 1993#Alias Conics ## 1994#Line # conic section defined by three points and a weight ## 1995 1996Conic describes a conical section: a piece of an ellipse, or a piece of a 1997parabola, or a piece of a hyperbola. Conic begins at a start Point, 1998curves towards a control Point, and then curves to an end Point. The influence 1999of the control Point is determined by Conic_Weight. 2000 2001Each Conic in Path adds two Points and one Conic_Weight. Conic_Weights in Path 2002may be inspected with Iter, or with RawIter. 2003 2004#Subtopic Weight 2005#Alias Conic_Weights ## 2006#Alias Weights ## 2007#Line # strength of Conic control Point ## 2008 2009Weight determines both the strength of the control Point and the type of Conic. 2010Weight varies from zero to infinity. At zero, Weight causes the control Point to 2011have no effect; Conic is identical to a line segment from start Point to end 2012point. If Weight is less than one, Conic follows an elliptical arc. 2013If Weight is exactly one, then Conic is identical to Quad; Conic follows a 2014parabolic arc. If Weight is greater than one, Conic follows a hyperbolic 2015arc. If Weight is infinity, Conic is identical to two line segments, connecting 2016start Point to control Point, and control Point to end Point. 2017 2018#Example 2019#Description 2020When Conic_Weight is one, Quad is added to path; the two are identical. 2021## 2022void draw(SkCanvas* canvas) { 2023 const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; 2024 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 2025 SkPath path; 2026 path.conicTo(20, 30, 50, 60, 1); 2027 SkPath::Iter iter(path, false); 2028 SkPath::Verb verb; 2029 do { 2030 SkPoint points[4]; 2031 verb = iter.next(points); 2032 SkDebugf("%s ", verbNames[(int) verb]); 2033 for (int i = 0; i < pointCount[(int) verb]; ++i) { 2034 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); 2035 } 2036 if (SkPath::kConic_Verb == verb) { 2037 SkDebugf("weight = %g", iter.conicWeight()); 2038 } 2039 SkDebugf("\n"); 2040 } while (SkPath::kDone_Verb != verb); 2041} 2042#StdOut 2043move {0, 0}, 2044quad {0, 0}, {20, 30}, {50, 60}, 2045done 2046## 2047## 2048 2049If weight is less than one, Conic is an elliptical segment. 2050 2051#Example 2052#Description 2053A 90 degree circular arc has the weight #Formula # 1 / sqrt(2) ##. 2054## 2055void draw(SkCanvas* canvas) { 2056 const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; 2057 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 2058 SkPath path; 2059 path.arcTo(20, 0, 20, 20, 20); 2060 SkPath::Iter iter(path, false); 2061 SkPath::Verb verb; 2062 do { 2063 SkPoint points[4]; 2064 verb = iter.next(points); 2065 SkDebugf("%s ", verbNames[(int) verb]); 2066 for (int i = 0; i < pointCount[(int) verb]; ++i) { 2067 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); 2068 } 2069 if (SkPath::kConic_Verb == verb) { 2070 SkDebugf("weight = %g", iter.conicWeight()); 2071 } 2072 SkDebugf("\n"); 2073 } while (SkPath::kDone_Verb != verb); 2074} 2075#StdOut 2076move {0, 0}, 2077conic {0, 0}, {20, 0}, {20, 20}, weight = 0.707107 2078done 2079## 2080## 2081 2082If weight is greater than one, Conic is a hyperbolic segment. As weight gets large, 2083a hyperbolic segment can be approximated by straight lines connecting the 2084control Point with the end Points. 2085 2086#Example 2087void draw(SkCanvas* canvas) { 2088 const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" }; 2089 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 2090 SkPath path; 2091 path.conicTo(20, 0, 20, 20, SK_ScalarInfinity); 2092 SkPath::Iter iter(path, false); 2093 SkPath::Verb verb; 2094 do { 2095 SkPoint points[4]; 2096 verb = iter.next(points); 2097 SkDebugf("%s ", verbNames[(int) verb]); 2098 for (int i = 0; i < pointCount[(int) verb]; ++i) { 2099 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); 2100 } 2101 if (SkPath::kConic_Verb == verb) { 2102 SkDebugf("weight = %g", iter.conicWeight()); 2103 } 2104 SkDebugf("\n"); 2105 } while (SkPath::kDone_Verb != verb); 2106} 2107#StdOut 2108move {0, 0}, 2109line {0, 0}, {20, 0}, 2110line {20, 0}, {20, 20}, 2111done 2112## 2113## 2114 2115#Subtopic Weight ## 2116 2117#Method SkPath& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 2118 SkScalar w) 2119#In Conic 2120#In Build 2121#Line # appends Conic ## 2122#Populate 2123 2124#Example 2125 #Height 160 2126 #Description 2127 As weight increases, curve is pulled towards control point. 2128 The bottom two curves are elliptical; the next is parabolic; the 2129 top curve is hyperbolic. 2130 ## 2131void draw(SkCanvas* canvas) { 2132 SkPaint paint; 2133 paint.setAntiAlias(true); 2134 paint.setStyle(SkPaint::kStroke_Style); 2135 SkPoint conicPts[] = {{20, 150}, {120, 10}, {220, 150}}; 2136 canvas->drawLine(conicPts[0], conicPts[1], paint); 2137 canvas->drawLine(conicPts[1], conicPts[2], paint); 2138 SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 }; 2139 paint.setStrokeWidth(3); 2140 SkScalar weight = 0.5f; 2141 for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) { 2142 SkPath path; 2143 path.moveTo(conicPts[0]); 2144 path.conicTo(conicPts[1], conicPts[2], weight); 2145 paint.setColor(colors[i]); 2146 canvas->drawPath(path, paint); 2147 weight += 0.25f; 2148 } 2149} 2150 ## 2151 2152 #SeeAlso rConicTo arcTo addArc quadTo 2153 2154## 2155 2156#Method SkPath& conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) 2157#In Build 2158#In Conic 2159#Populate 2160 2161#Example 2162 #Height 128 2163 #Description 2164 Conics and arcs use identical representations. As the arc sweep increases 2165 the Conic_Weight also increases, but remains smaller than one. 2166 ## 2167void draw(SkCanvas* canvas) { 2168 SkPaint paint; 2169 paint.setAntiAlias(true); 2170 paint.setStyle(SkPaint::kStroke_Style); 2171 SkRect oval = {0, 20, 120, 140}; 2172 SkPath path; 2173 for (int i = 0; i < 4; ++i) { 2174 path.moveTo(oval.centerX(), oval.fTop); 2175 path.arcTo(oval, -90, 90 - 20 * i, false); 2176 oval.inset(15, 15); 2177 } 2178 path.offset(100, 0); 2179 SkScalar conicWeights[] = { 0.707107f, 0.819152f, 0.906308f, 0.965926f }; 2180 SkPoint conicPts[][3] = { { {40, 20}, {100, 20}, {100, 80} }, 2181 { {40, 35}, {71.509f, 35}, {82.286f, 64.6091f} }, 2182 { {40, 50}, {53.9892f, 50}, {62.981f, 60.7164f} }, 2183 { {40, 65}, {44.0192f, 65}, {47.5f, 67.0096f} } }; 2184 for (int i = 0; i < 4; ++i) { 2185 path.moveTo(conicPts[i][0]); 2186 path.conicTo(conicPts[i][1], conicPts[i][2], conicWeights[i]); 2187 } 2188 canvas->drawPath(path, paint); 2189} 2190 ## 2191 2192 #SeeAlso rConicTo arcTo addArc quadTo 2193 2194## 2195 2196#Method SkPath& rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, 2197 SkScalar w) 2198#In Build 2199#In Conic 2200#Line # appends Conic relative to Last_Point ## 2201#Populate 2202 2203#Example 2204 #Height 140 2205 void draw(SkCanvas* canvas) { 2206 SkPaint paint; 2207 paint.setAntiAlias(true); 2208 paint.setStyle(SkPaint::kStroke_Style); 2209 SkPath path; 2210 path.moveTo(20, 80); 2211 path.rConicTo( 60, 0, 60, 60, 0.707107f); 2212 path.rConicTo( 0, -60, 60, -60, 0.707107f); 2213 path.rConicTo(-60, 0, -60, -60, 0.707107f); 2214 path.rConicTo( 0, 60, -60, 60, 0.707107f); 2215 canvas->drawPath(path, paint); 2216 } 2217 ## 2218 2219 #SeeAlso conicTo arcTo addArc quadTo 2220 2221## 2222 2223#Subtopic Conic ## 2224 2225# ------------------------------------------------------------------------------ 2226#Subtopic Cubic 2227#Alias Cubic ## 2228#Alias Cubics ## 2229#Alias Cubic_Bezier ## 2230#Alias Cubic_Beziers ## 2231#Line # curve described by third-order polynomial ## 2232 2233Cubic describes a Bezier_Curve segment described by a third-order polynomial. 2234Cubic begins at a start Point, curving towards the first control Point; 2235and curves from the end Point towards the second control Point. 2236 2237#Example 2238#Height 160 2239void draw(SkCanvas* canvas) { 2240 SkPaint paint; 2241 paint.setAntiAlias(true); 2242 paint.setStyle(SkPaint::kStroke_Style); 2243 SkPoint cubicPts[] = {{20, 150}, {90, 10}, {160, 150}, {230, 10}}; 2244 SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 }; 2245 for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) { 2246 paint.setColor(0x7fffffff & colors[i]); 2247 paint.setStrokeWidth(1); 2248 for (unsigned j = 0; j < 3; ++j) { 2249 canvas->drawLine(cubicPts[j], cubicPts[j + 1], paint); 2250 } 2251 SkPath path; 2252 path.moveTo(cubicPts[0]); 2253 path.cubicTo(cubicPts[1], cubicPts[2], cubicPts[3]); 2254 paint.setStrokeWidth(3); 2255 paint.setColor(colors[i]); 2256 canvas->drawPath(path, paint); 2257 cubicPts[1].fY += 30; 2258 cubicPts[2].fX += 30; 2259 } 2260} 2261## 2262 2263#Method SkPath& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 2264 SkScalar x3, SkScalar y3) 2265#In Build 2266#In Cubic 2267#Line # appends Cubic ## 2268#Populate 2269 2270#Example 2271void draw(SkCanvas* canvas) { 2272 SkPaint paint; 2273 paint.setAntiAlias(true); 2274 paint.setStyle(SkPaint::kStroke_Style); 2275 SkPath path; 2276 path.moveTo(0, -10); 2277 for (int i = 0; i < 128; i += 16) { 2278 SkScalar c = i * 0.5f; 2279 path.cubicTo( 10 + c, -10 - i, 10 + i, -10 - c, 10 + i, 0); 2280 path.cubicTo( 14 + i, 14 + c, 14 + c, 14 + i, 0, 14 + i); 2281 path.cubicTo(-18 - c, 18 + i, -18 - i, 18 + c, -18 - i, 0); 2282 path.cubicTo(-22 - i, -22 - c, -22 - c, -22 - i, 0, -22 - i); 2283 } 2284 path.offset(128, 128); 2285 canvas->drawPath(path, paint); 2286} 2287## 2288 2289#SeeAlso Contour moveTo rCubicTo quadTo 2290 2291## 2292 2293# ------------------------------------------------------------------------------ 2294 2295#Method SkPath& cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3) 2296 2297#In Build 2298#In Cubic 2299#Populate 2300 2301#Example 2302#Height 84 2303 SkPaint paint; 2304 paint.setAntiAlias(true); 2305 paint.setStyle(SkPaint::kStroke_Style); 2306 SkPoint pts[] = { {20, 20}, {300, 80}, {-140, 90}, {220, 10} }; 2307 SkPath path; 2308 path.moveTo(pts[0]); 2309 path.cubicTo(pts[1], pts[2], pts[3]); 2310 canvas->drawPath(path, paint); 2311## 2312 2313#SeeAlso Contour moveTo rCubicTo quadTo 2314 2315## 2316 2317# ------------------------------------------------------------------------------ 2318 2319#Method SkPath& rCubicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, 2320 SkScalar dx3, SkScalar dy3) 2321#In Build 2322#In Cubic 2323#Line # appends Cubic relative to Last_Point ## 2324#Populate 2325 2326#Example 2327 void draw(SkCanvas* canvas) { 2328 SkPaint paint; 2329 paint.setAntiAlias(true); 2330 paint.setStyle(SkPaint::kStroke_Style); 2331 SkPath path; 2332 path.moveTo(24, 108); 2333 for (int i = 0; i < 16; i++) { 2334 SkScalar sx, sy; 2335 sx = SkScalarSinCos(i * SK_ScalarPI / 8, &sy); 2336 path.rCubicTo(40 * sx, 4 * sy, 4 * sx, 40 * sy, 40 * sx, 40 * sy); 2337 } 2338 canvas->drawPath(path, paint); 2339 } 2340## 2341 2342#SeeAlso Contour moveTo cubicTo quadTo 2343 2344## 2345 2346#Subtopic Cubic ## 2347 2348# ------------------------------------------------------------------------------ 2349 2350#Subtopic Arc 2351#Line # part of Oval or Circle ## 2352Arc can be constructed in a number of ways. Arc may be described by part of Oval and angles, 2353by start point and end point, and by radius and tangent lines. Each construction has advantages, 2354and some constructions correspond to Arc drawing in graphics standards. 2355 2356All Arc draws are implemented by one or more Conic draws. When Conic_Weight is less than one, 2357Conic describes an Arc of some Oval or Circle. 2358 2359arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) 2360describes Arc as a piece of Oval, beginning at start angle, sweeping clockwise or counterclockwise, 2361which may continue Contour or start a new one. This construction is similar to PostScript and 2362HTML_Canvas arcs. Variation addArc always starts new Contour. SkCanvas::drawArc draws without 2363requiring Path. 2364 2365arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) 2366describes Arc as tangent to the line segment from last Point added to Path to (x1, y1); and tangent 2367to the line segment from (x1, y1) to (x2, y2). This construction is similar to PostScript and 2368HTML_Canvas arcs. 2369 2370arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep, 2371 SkScalar x, SkScalar y) 2372describes Arc as part of Oval with radii (rx, ry), beginning at 2373last Point added to Path and ending at (x, y). More than one Arc satisfies this criteria, 2374so additional values choose a single solution. This construction is similar to SVG arcs. 2375 2376conicTo describes Arc of less than 180 degrees as a pair of tangent lines and Conic_Weight. 2377conicTo can represent any Arc with a sweep less than 180 degrees at any rotation. All arcTo 2378constructions are converted to Conic data when added to Path. 2379 2380#ToDo example is spaced correctly on fiddle but spacing is too wide on pc 2381## 2382 2383#Illustration 2384 2385#List 2386# <sup>1</sup> arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) ## 2387# <sup>2</sup> parameter adds move to first point ## 2388# <sup>3</sup> start angle must be multiple of 90 degrees ## 2389# <sup>4</sup> arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) ## 2390# <sup>5</sup> arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, 2391 Direction sweep, SkScalar x, SkScalar y) ## 2392#List ## 2393 2394#Example 2395#Height 128 2396void draw(SkCanvas* canvas) { 2397 SkRect oval = {8, 8, 56, 56}; 2398 SkPaint ovalPaint; 2399 ovalPaint.setAntiAlias(true); 2400 SkPaint textPaint(ovalPaint); 2401 ovalPaint.setStyle(SkPaint::kStroke_Style); 2402 SkPaint arcPaint(ovalPaint); 2403 arcPaint.setStrokeWidth(5); 2404 arcPaint.setColor(SK_ColorBLUE); 2405 canvas->translate(-64, 0); 2406 for (char arcStyle = '1'; arcStyle <= '6'; ++arcStyle) { 2407 '4' == arcStyle ? canvas->translate(-96, 55) : canvas->translate(64, 0); 2408 canvas->drawText(&arcStyle, 1, 30, 36, textPaint); 2409 canvas->drawOval(oval, ovalPaint); 2410 SkPath path; 2411 path.moveTo({56, 32}); 2412 switch (arcStyle) { 2413 case '1': 2414 path.arcTo(oval, 0, 90, false); 2415 break; 2416 case '2': 2417 canvas->drawArc(oval, 0, 90, false, arcPaint); 2418 continue; 2419 case '3': 2420 path.addArc(oval, 0, 90); 2421 break; 2422 case '4': 2423 path.arcTo({56, 56}, {32, 56}, 24); 2424 break; 2425 case '5': 2426 path.arcTo({24, 24}, 0, SkPath::kSmall_ArcSize, SkPath::kCW_Direction, {32, 56}); 2427 break; 2428 case '6': 2429 path.conicTo({56, 56}, {32, 56}, SK_ScalarRoot2Over2); 2430 break; 2431 } 2432 canvas->drawPath(path, arcPaint); 2433 } 2434} 2435#Example ## 2436 2437In the example above: 2438#List 2439# 1 describes an arc from an oval, a starting angle, and a sweep angle. ## 2440# 2 is similar to 1, but does not require building a path to draw. ## 2441# 3 is similar to 1, but always begins new Contour. ## 2442# 4 describes an arc from a pair of tangent lines and a radius. ## 2443# 5 describes an arc from Oval center, arc start Point and arc end Point. ## 2444# 6 describes an arc from a pair of tangent lines and a Conic_Weight. ## 2445#List ## 2446 2447#Method SkPath& arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) 2448#In Build 2449#In Arc 2450#Line # appends Arc ## 2451#Populate 2452 2453#Example 2454#Height 200 2455#Description 2456arcTo continues a previous contour when forceMoveTo is false and when Path 2457is not empty. 2458## 2459void draw(SkCanvas* canvas) { 2460 SkPaint paint; 2461 SkPath path; 2462 paint.setStyle(SkPaint::kStroke_Style); 2463 paint.setStrokeWidth(4); 2464 path.moveTo(0, 0); 2465 path.arcTo({20, 20, 120, 120}, -90, 90, false); 2466 canvas->drawPath(path, paint); 2467 path.rewind(); 2468 path.arcTo({120, 20, 220, 120}, -90, 90, false); 2469 canvas->drawPath(path, paint); 2470 path.rewind(); 2471 path.moveTo(0, 0); 2472 path.arcTo({20, 120, 120, 220}, -90, 90, true); 2473 canvas->drawPath(path, paint); 2474} 2475## 2476 2477#SeeAlso addArc SkCanvas::drawArc conicTo 2478 2479## 2480 2481# ------------------------------------------------------------------------------ 2482 2483#Method SkPath& arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) 2484#In Build 2485#In Arc 2486#Populate 2487 2488#Example 2489#Height 226 2490void draw(SkCanvas* canvas) { 2491 SkPaint tangentPaint; 2492 tangentPaint.setAntiAlias(true); 2493 SkPaint textPaint(tangentPaint); 2494 tangentPaint.setStyle(SkPaint::kStroke_Style); 2495 tangentPaint.setColor(SK_ColorGRAY); 2496 SkPaint arcPaint(tangentPaint); 2497 arcPaint.setStrokeWidth(5); 2498 arcPaint.setColor(SK_ColorBLUE); 2499 SkPath path; 2500 SkPoint pts[] = { {56, 20}, {200, 20}, {90, 190} }; 2501 SkScalar radius = 50; 2502 path.moveTo(pts[0]); 2503 path.arcTo(pts[1], pts[2], radius); 2504 canvas->drawLine(pts[0], pts[1], tangentPaint); 2505 canvas->drawLine(pts[1], pts[2], tangentPaint); 2506 SkPoint lastPt; 2507 (void) path.getLastPt(&lastPt); 2508 SkVector radial = pts[2] - pts[1]; 2509 radial.setLength(radius); 2510 SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX }; 2511 canvas->drawCircle(center, radius, tangentPaint); 2512 canvas->drawLine(lastPt, center, tangentPaint); 2513 radial = pts[1] - pts[0]; 2514 radial.setLength(radius); 2515 SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX }; 2516 canvas->drawLine(center, arcStart, tangentPaint); 2517 canvas->drawPath(path, arcPaint); 2518 canvas->drawString("(x0, y0)", pts[0].fX - 5, pts[0].fY, textPaint); 2519 canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint); 2520 canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint); 2521 canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint); 2522 canvas->drawString("radius", center.fX - 3, center.fY - 16, textPaint); 2523} 2524## 2525 2526#Example 2527#Height 128 2528void draw(SkCanvas* canvas) { 2529 SkPaint tangentPaint; 2530 tangentPaint.setAntiAlias(true); 2531 SkPaint textPaint(tangentPaint); 2532 tangentPaint.setStyle(SkPaint::kStroke_Style); 2533 tangentPaint.setColor(SK_ColorGRAY); 2534 SkPaint arcPaint(tangentPaint); 2535 arcPaint.setStrokeWidth(5); 2536 arcPaint.setColor(SK_ColorBLUE); 2537 SkPath path; 2538 SkPoint pts[] = { {156, 20}, {200, 20}, {170, 50} }; 2539 SkScalar radius = 50; 2540 path.moveTo(pts[0]); 2541 path.arcTo(pts[1], pts[2], radius); 2542 canvas->drawLine(pts[0], pts[1], tangentPaint); 2543 canvas->drawLine(pts[1], pts[2], tangentPaint); 2544 SkPoint lastPt; 2545 (void) path.getLastPt(&lastPt); 2546 SkVector radial = pts[2] - pts[1]; 2547 radial.setLength(radius); 2548 SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX }; 2549 canvas->drawLine(lastPt, center, tangentPaint); 2550 radial = pts[1] - pts[0]; 2551 radial.setLength(radius); 2552 SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX }; 2553 canvas->drawLine(center, arcStart, tangentPaint); 2554 canvas->drawPath(path, arcPaint); 2555 canvas->drawString("(x0, y0)", pts[0].fX, pts[0].fY - 7, textPaint); 2556 canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint); 2557 canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint); 2558 canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint); 2559 canvas->drawString("radius", center.fX - 5, center.fY - 20, textPaint); 2560} 2561## 2562 2563#Example 2564#Description 2565arcTo is represented by Line and circular Conic in Path. 2566## 2567void draw(SkCanvas* canvas) { 2568 SkPath path; 2569 path.moveTo({156, 20}); 2570 path.arcTo(200, 20, 170, 50, 50); 2571 SkPath::Iter iter(path, false); 2572 SkPoint p[4]; 2573 SkPath::Verb verb; 2574 while (SkPath::kDone_Verb != (verb = iter.next(p))) { 2575 switch (verb) { 2576 case SkPath::kMove_Verb: 2577 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY); 2578 break; 2579 case SkPath::kLine_Verb: 2580 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY); 2581 break; 2582 case SkPath::kConic_Verb: 2583 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n", 2584 p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight()); 2585 break; 2586 default: 2587 SkDebugf("unexpected verb\n"); 2588 } 2589 } 2590} 2591#StdOut 2592move to (156,20) 2593line (156,20),(79.2893,20) 2594conic (79.2893,20),(200,20),(114.645,105.355) weight 0.382683 2595## 2596## 2597 2598#SeeAlso conicTo 2599 2600## 2601 2602# ------------------------------------------------------------------------------ 2603 2604#Method SkPath& arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius) 2605#In Build 2606#In Arc 2607#Populate 2608 2609#Example 2610#Description 2611Because tangent lines are parallel, arcTo appends line from last Path Point to 2612p1, but does not append a circular Conic. 2613## 2614void draw(SkCanvas* canvas) { 2615 SkPath path; 2616 path.moveTo({156, 20}); 2617 path.arcTo({200, 20}, {170, 20}, 50); 2618 SkPath::Iter iter(path, false); 2619 SkPoint p[4]; 2620 SkPath::Verb verb; 2621 while (SkPath::kDone_Verb != (verb = iter.next(p))) { 2622 switch (verb) { 2623 case SkPath::kMove_Verb: 2624 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY); 2625 break; 2626 case SkPath::kLine_Verb: 2627 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY); 2628 break; 2629 case SkPath::kConic_Verb: 2630 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n", 2631 p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight()); 2632 break; 2633 default: 2634 SkDebugf("unexpected verb\n"); 2635 } 2636 } 2637} 2638#StdOut 2639move to (156,20) 2640line (156,20),(200,20) 2641## 2642## 2643 2644#SeeAlso conicTo 2645 2646## 2647 2648# ------------------------------------------------------------------------------ 2649 2650#Enum ArcSize 2651#Line # used by arcTo variation ## 2652 2653#Code 2654 enum ArcSize { 2655 kSmall_ArcSize, 2656 kLarge_ArcSize, 2657 }; 2658## 2659 2660Four axis-aligned Ovals with the same height and width intersect a pair of Points. 2661ArcSize and Direction select one of the four Ovals, by choosing the larger or smaller 2662arc between the Points; and by choosing the arc Direction, clockwise 2663or counterclockwise. 2664 2665#Const kSmall_ArcSize 0 2666#Line # smaller of Arc pair ## 2667## 2668#Const kLarge_ArcSize 1 2669#Line # larger of Arc pair ## 2670## 2671 2672#Example 2673#Height 160 2674#Description 2675Arc begins at top of Oval pair and ends at bottom. Arc can take four routes to get there. 2676Two routes are large, and two routes are counterclockwise. The one route both large 2677and counterclockwise is blue. 2678## 2679void draw(SkCanvas* canvas) { 2680 SkPaint paint; 2681 paint.setAntiAlias(true); 2682 paint.setStyle(SkPaint::kStroke_Style); 2683 for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { 2684 for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) { 2685 SkPath path; 2686 path.moveTo({120, 50}); 2687 path.arcTo(70, 40, 30, arcSize, sweep, 156, 100); 2688 if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) { 2689 paint.setColor(SK_ColorBLUE); 2690 paint.setStrokeWidth(3); 2691 } 2692 canvas->drawPath(path, paint); 2693 } 2694 } 2695} 2696## 2697 2698#SeeAlso arcTo Direction 2699 2700## 2701 2702# ------------------------------------------------------------------------------ 2703 2704#Method SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, 2705 Direction sweep, SkScalar x, SkScalar y) 2706#In Build 2707#In Arc 2708#Populate 2709 2710#Example 2711#Height 160 2712void draw(SkCanvas* canvas) { 2713 SkPaint paint; 2714 paint.setAntiAlias(true); 2715 paint.setStyle(SkPaint::kStroke_Style); 2716 for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { 2717 for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) { 2718 SkPath path; 2719 path.moveTo({120, 50}); 2720 path.arcTo(70, 40, 30, arcSize, sweep, 120.1, 50); 2721 if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) { 2722 paint.setColor(SK_ColorBLUE); 2723 paint.setStrokeWidth(3); 2724 } 2725 canvas->drawPath(path, paint); 2726 } 2727 } 2728} 2729## 2730 2731#SeeAlso rArcTo ArcSize Direction 2732 2733## 2734 2735# ------------------------------------------------------------------------------ 2736 2737#Method SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep, 2738 const SkPoint xy) 2739#In Build 2740#In Arc 2741#Populate 2742 2743#Example 2744#Height 108 2745void draw(SkCanvas* canvas) { 2746 SkPaint paint; 2747 SkPath path; 2748 const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}}; 2749 for (auto start : starts) { 2750 path.moveTo(start.fX, start.fY); 2751 path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0); 2752 } 2753 canvas->drawPath(path, paint); 2754} 2755## 2756 2757#SeeAlso rArcTo ArcSize Direction 2758 2759## 2760 2761# ------------------------------------------------------------------------------ 2762 2763#Method SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, 2764 Direction sweep, SkScalar dx, SkScalar dy) 2765#In Build 2766#In Arc 2767#Line # appends Arc relative to Last_Point ## 2768 2769Appends Arc to Path, relative to last Path Point. Arc is implemented by one or 2770more Conic, weighted to describe part of Oval with radii (rx, ry) rotated by 2771xAxisRotate degrees. Arc curves from last Path Point to relative end Point 2772(dx, dy), choosing one of four possible routes: clockwise or 2773counterclockwise, and smaller or larger. If Path is empty, the start Arc Point 2774is (0, 0). 2775 2776Arc sweep is always less than 360 degrees. arcTo appends Line to end Point 2777if either radii are zero, or if last Path Point equals end Point. 2778arcTo scales radii (rx, ry) to fit last Path Point and end Point if both are 2779greater than zero but too small to describe an arc. 2780 2781arcTo appends up to four Conic curves. 2782arcTo implements the functionality of SVG_Arc, although SVG "sweep-flag" value is 2783opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while 2784kCW_Direction cast to int is zero. 2785 2786#Param rx radius before x-axis rotation ## 2787#Param ry radius before x-axis rotation ## 2788#Param xAxisRotate x-axis rotation in degrees; positive values are clockwise ## 2789#Param largeArc chooses smaller or larger Arc ## 2790#Param sweep chooses clockwise or counterclockwise Arc ## 2791#Param dx x-axis offset end of Arc from last Path Point ## 2792#Param dy y-axis offset end of Arc from last Path Point ## 2793 2794#Return reference to Path ## 2795 2796#Example 2797#Height 108 2798void draw(SkCanvas* canvas) { 2799 SkPaint paint; 2800 SkPath path; 2801 const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}}; 2802 for (auto start : starts) { 2803 path.moveTo(start.fX, start.fY); 2804 path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0); 2805 } 2806 canvas->drawPath(path, paint); 2807} 2808## 2809 2810#SeeAlso arcTo ArcSize Direction 2811 2812## 2813 2814#Subtopic Arc ## 2815 2816# ------------------------------------------------------------------------------ 2817 2818#Method SkPath& close() 2819#In Build 2820#Line # makes last Contour a loop ## 2821#Populate 2822 2823#Example 2824void draw(SkCanvas* canvas) { 2825 SkPaint paint; 2826 paint.setStrokeWidth(15); 2827 paint.setStrokeCap(SkPaint::kRound_Cap); 2828 SkPath path; 2829 const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}}; 2830 path.addPoly(points, SK_ARRAY_COUNT(points), false); 2831 for (int loop = 0; loop < 2; ++loop) { 2832 for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style, 2833 SkPaint::kStrokeAndFill_Style} ) { 2834 paint.setStyle(style); 2835 canvas->drawPath(path, paint); 2836 canvas->translate(85, 0); 2837 } 2838 path.close(); 2839 canvas->translate(-255, 128); 2840 } 2841} 2842## 2843 2844#SeeAlso 2845 2846## 2847 2848# ------------------------------------------------------------------------------ 2849 2850#Method static bool IsInverseFillType(FillType fill) 2851#In Property 2852#Line # returns if Fill_Type represents outside geometry ## 2853Returns true if fill is inverted and Path with fill represents area outside 2854of its geometric bounds. 2855 2856#Table 2857#Legend 2858# FillType # is inverse ## 2859## 2860# kWinding_FillType # false ## 2861# kEvenOdd_FillType # false ## 2862# kInverseWinding_FillType # true ## 2863# kInverseEvenOdd_FillType # true ## 2864## 2865 2866#Param fill one of: kWinding_FillType, kEvenOdd_FillType, 2867 kInverseWinding_FillType, kInverseEvenOdd_FillType 2868## 2869 2870#Return true if Path fills outside its bounds ## 2871 2872#Example 2873#Function 2874###$ 2875#define nameValue(fill) { SkPath::fill, #fill } 2876 2877$$$# 2878## 2879void draw(SkCanvas* canvas) { 2880 struct { 2881 SkPath::FillType fill; 2882 const char* name; 2883 } fills[] = { 2884 nameValue(kWinding_FillType), 2885 nameValue(kEvenOdd_FillType), 2886 nameValue(kInverseWinding_FillType), 2887 nameValue(kInverseEvenOdd_FillType), 2888 }; 2889 for (auto fill: fills ) { 2890 SkDebugf("IsInverseFillType(%s) == %s\n", fill.name, SkPath::IsInverseFillType(fill.fill) ? 2891 "true" : "false"); 2892 } 2893} 2894#StdOut 2895IsInverseFillType(kWinding_FillType) == false 2896IsInverseFillType(kEvenOdd_FillType) == false 2897IsInverseFillType(kInverseWinding_FillType) == true 2898IsInverseFillType(kInverseEvenOdd_FillType) == true 2899## 2900## 2901 2902#SeeAlso FillType getFillType setFillType ConvertToNonInverseFillType 2903 2904## 2905 2906# ------------------------------------------------------------------------------ 2907 2908#Method static FillType ConvertToNonInverseFillType(FillType fill) 2909#In Utility 2910#Line # returns Fill_Type representing inside geometry ## 2911Returns equivalent Fill_Type representing Path fill inside its bounds. 2912 2913#Table 2914#Legend 2915# FillType # inside FillType ## 2916## 2917# kWinding_FillType # kWinding_FillType ## 2918# kEvenOdd_FillType # kEvenOdd_FillType ## 2919# kInverseWinding_FillType # kWinding_FillType ## 2920# kInverseEvenOdd_FillType # kEvenOdd_FillType ## 2921## 2922 2923#Param fill one of: kWinding_FillType, kEvenOdd_FillType, 2924 kInverseWinding_FillType, kInverseEvenOdd_FillType 2925## 2926 2927#Return fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted ## 2928 2929#Example 2930#Function 2931###$ 2932#define nameValue(fill) { SkPath::fill, #fill } 2933 2934$$$# 2935## 2936void draw(SkCanvas* canvas) { 2937 struct { 2938 SkPath::FillType fill; 2939 const char* name; 2940 } fills[] = { 2941 nameValue(kWinding_FillType), 2942 nameValue(kEvenOdd_FillType), 2943 nameValue(kInverseWinding_FillType), 2944 nameValue(kInverseEvenOdd_FillType), 2945 }; 2946 for (unsigned i = 0; i < SK_ARRAY_COUNT(fills); ++i) { 2947 if (fills[i].fill != (SkPath::FillType) i) { 2948 SkDebugf("fills array order does not match FillType enum order"); 2949 break; 2950 } 2951 SkDebugf("ConvertToNonInverseFillType(%s) == %s\n", fills[i].name, 2952 fills[(int) SkPath::ConvertToNonInverseFillType(fills[i].fill)].name); 2953 } 2954} 2955#StdOut 2956ConvertToNonInverseFillType(kWinding_FillType) == kWinding_FillType 2957ConvertToNonInverseFillType(kEvenOdd_FillType) == kEvenOdd_FillType 2958ConvertToNonInverseFillType(kInverseWinding_FillType) == kWinding_FillType 2959ConvertToNonInverseFillType(kInverseEvenOdd_FillType) == kEvenOdd_FillType 2960## 2961## 2962 2963#SeeAlso FillType getFillType setFillType IsInverseFillType 2964 2965## 2966 2967# ------------------------------------------------------------------------------ 2968 2969#Method static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, 2970 SkScalar w, SkPoint pts[], int pow2) 2971#In Utility 2972#Line # approximates Conic with Quad array ## 2973 2974Approximates Conic with Quad array. Conic is constructed from start Point p0, 2975control Point p1, end Point p2, and weight w. 2976Quad array is stored in pts; this storage is supplied by caller. 2977Maximum Quad count is 2 to the pow2. 2978Every third point in array shares last Point of previous Quad and first Point of 2979next Quad. Maximum pts storage size is given by: 2980#Formula # (1 + 2 * (1 << pow2)) * sizeof(SkPoint) ##. 2981 2982Returns Quad count used the approximation, which may be smaller 2983than the number requested. 2984 2985Conic_Weight determines the amount of influence Conic control point has on the curve. 2986w less than one represents an elliptical section. w greater than one represents 2987a hyperbolic section. w equal to one represents a parabolic section. 2988 2989Two Quad curves are sufficient to approximate an elliptical Conic with a sweep 2990of up to 90 degrees; in this case, set pow2 to one. 2991 2992#Param p0 Conic start Point ## 2993#Param p1 Conic control Point ## 2994#Param p2 Conic end Point ## 2995#Param w Conic weight ## 2996#Param pts storage for Quad array ## 2997#Param pow2 Quad count, as power of two, normally 0 to 5 (1 to 32 Quad curves) ## 2998 2999#Return number of Quad curves written to pts ## 3000 3001#Example 3002#Description 3003A pair of Quad curves are drawn in red on top of the elliptical Conic curve in black. 3004The middle curve is nearly circular. The top-right curve is parabolic, which can 3005be drawn exactly with a single Quad. 3006## 3007void draw(SkCanvas* canvas) { 3008 SkPaint conicPaint; 3009 conicPaint.setAntiAlias(true); 3010 conicPaint.setStyle(SkPaint::kStroke_Style); 3011 SkPaint quadPaint(conicPaint); 3012 quadPaint.setColor(SK_ColorRED); 3013 SkPoint conic[] = { {20, 170}, {80, 170}, {80, 230} }; 3014 for (auto weight : { .25f, .5f, .707f, .85f, 1.f } ) { 3015 SkPoint quads[5]; 3016 SkPath::ConvertConicToQuads(conic[0], conic[1], conic[2], weight, quads, 1); 3017 SkPath path; 3018 path.moveTo(conic[0]); 3019 path.conicTo(conic[1], conic[2], weight); 3020 canvas->drawPath(path, conicPaint); 3021 path.rewind(); 3022 path.moveTo(quads[0]); 3023 path.quadTo(quads[1], quads[2]); 3024 path.quadTo(quads[3], quads[4]); 3025 canvas->drawPath(path, quadPaint); 3026 canvas->translate(50, -50); 3027 } 3028} 3029## 3030 3031#SeeAlso Conic Quad 3032 3033## 3034 3035# ------------------------------------------------------------------------------ 3036 3037#Method bool isRect(SkRect* rect, bool* isClosed = nullptr, Direction* direction = nullptr) const 3038#In Property 3039#Line # returns if describes Rect ## 3040#Populate 3041 3042#Example 3043#Description 3044After addRect, isRect returns true. Following moveTo permits isRect to return true, but 3045following lineTo does not. addPoly returns true even though rect is not closed, and one 3046side of rect is made up of consecutive line segments. 3047## 3048void draw(SkCanvas* canvas) { 3049 auto debugster = [](const char* prefix, const SkPath& path) -> void { 3050 SkRect rect; 3051 SkPath::Direction direction; 3052 bool isClosed; 3053 path.isRect(&rect, &isClosed, &direction) ? 3054 SkDebugf("%s is rect (%g, %g, %g, %g); is %s" "closed; direction %s\n", prefix, 3055 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, isClosed ? "" : "not ", 3056 SkPath::kCW_Direction == direction ? "CW" : "CCW") : 3057 SkDebugf("%s is not rect\n", prefix); 3058 }; 3059 SkPath path; 3060 debugster("empty", path); 3061 path.addRect({10, 20, 30, 40}); 3062 debugster("addRect", path); 3063 path.moveTo(60, 70); 3064 debugster("moveTo", path); 3065 path.lineTo(60, 70); 3066 debugster("lineTo", path); 3067 path.reset(); 3068 const SkPoint pts[] = { {0, 0}, {0, 80}, {80, 80}, {80, 0}, {40, 0}, {20, 0} }; 3069 path.addPoly(pts, SK_ARRAY_COUNT(pts), false); 3070 debugster("addPoly", path); 3071} 3072#StdOut 3073empty is not rect 3074addRect is rect (10, 20, 30, 40); is closed; direction CW 3075moveTo is rect (10, 20, 30, 40); is closed; direction CW 3076lineTo is not rect 3077addPoly is rect (0, 0, 80, 80); is not closed; direction CCW 3078## 3079## 3080 3081#SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isNestedFillRects 3082 3083## 3084 3085# ------------------------------------------------------------------------------ 3086 3087#Method bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = nullptr) const 3088#In Property 3089#Line # returns if describes Rect pair, one inside the other ## 3090#Populate 3091 3092#Example 3093void draw(SkCanvas* canvas) { 3094 SkPaint paint; 3095 paint.setStyle(SkPaint::kStroke_Style); 3096 paint.setStrokeWidth(5); 3097 SkPath path; 3098 path.addRect({10, 20, 30, 40}); 3099 paint.getFillPath(path, &path); 3100 SkRect rects[2]; 3101 SkPath::Direction directions[2]; 3102 if (path.isNestedFillRects(rects, directions)) { 3103 for (int i = 0; i < 2; ++i) { 3104 SkDebugf("%s (%g, %g, %g, %g); direction %s\n", i ? "inner" : "outer", 3105 rects[i].fLeft, rects[i].fTop, rects[i].fRight, rects[i].fBottom, 3106 SkPath::kCW_Direction == directions[i] ? "CW" : "CCW"); 3107 } 3108 } else { 3109 SkDebugf("is not nested rectangles\n"); 3110 } 3111} 3112#StdOut 3113outer (7.5, 17.5, 32.5, 42.5); direction CW 3114inner (12.5, 22.5, 27.5, 37.5); direction CCW 3115## 3116## 3117 3118#SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isRect 3119 3120## 3121 3122# ------------------------------------------------------------------------------ 3123 3124#Method SkPath& addRect(const SkRect& rect, Direction dir = kCW_Direction) 3125#In Build 3126#Line # adds one Contour containing Rect ## 3127#Populate 3128 3129#Example 3130#Description 3131The left Rect dashes starting at the top-left corner, to the right. 3132The right Rect dashes starting at the top-left corner, towards the bottom. 3133## 3134#Height 128 3135void draw(SkCanvas* canvas) { 3136 SkPaint paint; 3137 paint.setStrokeWidth(15); 3138 paint.setStrokeCap(SkPaint::kSquare_Cap); 3139 float intervals[] = { 5, 21.75f }; 3140 paint.setStyle(SkPaint::kStroke_Style); 3141 paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0)); 3142 SkPath path; 3143 path.addRect({20, 20, 100, 100}, SkPath::kCW_Direction); 3144 canvas->drawPath(path, paint); 3145 path.rewind(); 3146 path.addRect({140, 20, 220, 100}, SkPath::kCCW_Direction); 3147 canvas->drawPath(path, paint); 3148} 3149## 3150 3151#SeeAlso SkCanvas::drawRect Direction 3152 3153## 3154 3155# ------------------------------------------------------------------------------ 3156 3157#Method SkPath& addRect(const SkRect& rect, Direction dir, unsigned start) 3158 3159Adds Rect to Path, appending kMove_Verb, three kLine_Verb, and kClose_Verb. 3160If dir is kCW_Direction, Rect corners are added clockwise; if dir is 3161kCCW_Direction, Rect corners are added counterclockwise. 3162start determines the first corner added. 3163 3164#Table 3165#Legend 3166# start # first corner ## 3167#Legend ## 3168# 0 # top-left ## 3169# 1 # top-right ## 3170# 2 # bottom-right ## 3171# 3 # bottom-left ## 3172#Table ## 3173 3174#Param rect Rect to add as a closed contour ## 3175#Param dir Direction to wind added contour ## 3176#Param start initial corner of Rect to add ## 3177 3178#Return reference to Path ## 3179 3180#Example 3181#Height 128 3182#Description 3183The arrow is just after the initial corner and points towards the next 3184corner appended to Path. 3185## 3186void draw(SkCanvas* canvas) { 3187 const SkPoint arrow[] = { {5, -5}, {15, -5}, {20, 0}, {15, 5}, {5, 5}, {10, 0} }; 3188 const SkRect rect = {10, 10, 54, 54}; 3189 SkPaint rectPaint; 3190 rectPaint.setAntiAlias(true); 3191 rectPaint.setStyle(SkPaint::kStroke_Style); 3192 SkPaint arrowPaint(rectPaint); 3193 SkPath arrowPath; 3194 arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true); 3195 arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0, 3196 SkPath1DPathEffect::kRotate_Style)); 3197 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { 3198 for (unsigned start : { 0, 1, 2, 3 } ) { 3199 SkPath path; 3200 path.addRect(rect, direction, start); 3201 canvas->drawPath(path, rectPaint); 3202 canvas->drawPath(path, arrowPaint); 3203 canvas->translate(64, 0); 3204 } 3205 canvas->translate(-256, 64); 3206 } 3207} 3208## 3209 3210#SeeAlso SkCanvas::drawRect Direction 3211 3212## 3213 3214# ------------------------------------------------------------------------------ 3215 3216#Method SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom, 3217 Direction dir = kCW_Direction) 3218#Populate 3219 3220#Example 3221#Description 3222The left Rect dashes start at the top-left corner, and continue to the right. 3223The right Rect dashes start at the top-left corner, and continue down. 3224## 3225#Height 128 3226void draw(SkCanvas* canvas) { 3227 SkPaint paint; 3228 paint.setStrokeWidth(15); 3229 paint.setStrokeCap(SkPaint::kSquare_Cap); 3230 float intervals[] = { 5, 21.75f }; 3231 paint.setStyle(SkPaint::kStroke_Style); 3232 paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0)); 3233 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { 3234 SkPath path; 3235 path.addRect(20, 20, 100, 100, direction); 3236 canvas->drawPath(path, paint); 3237 canvas->translate(128, 0); 3238 } 3239} 3240## 3241 3242#SeeAlso SkCanvas::drawRect Direction 3243 3244## 3245 3246# ------------------------------------------------------------------------------ 3247 3248#Method SkPath& addOval(const SkRect& oval, Direction dir = kCW_Direction) 3249#In Build 3250#Line # adds one Contour containing Oval ## 3251#Populate 3252 3253#Example 3254#Height 120 3255 SkPaint paint; 3256 SkPath oval; 3257 oval.addOval({20, 20, 160, 80}); 3258 canvas->drawPath(oval, paint); 3259## 3260 3261#SeeAlso SkCanvas::drawOval Direction Oval 3262 3263## 3264 3265# ------------------------------------------------------------------------------ 3266 3267#Method SkPath& addOval(const SkRect& oval, Direction dir, unsigned start) 3268 3269Adds Oval to Path, appending kMove_Verb, four kConic_Verb, and kClose_Verb. 3270Oval is upright ellipse bounded by Rect oval with radii equal to half oval width 3271and half oval height. Oval begins at start and continues 3272clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. 3273 3274#Table 3275#Legend 3276# start # Point ## 3277#Legend ## 3278# 0 # oval.centerX(), oval.fTop ## 3279# 1 # oval.fRight, oval.centerY() ## 3280# 2 # oval.centerX(), oval.fBottom ## 3281# 3 # oval.fLeft, oval.centerY() ## 3282#Table ## 3283 3284#Param oval bounds of ellipse added ## 3285#Param dir Direction to wind ellipse ## 3286#Param start index of initial point of ellipse ## 3287 3288#Return reference to Path ## 3289 3290#Example 3291#Height 160 3292void draw(SkCanvas* canvas) { 3293 const SkPoint arrow[] = { {0, -5}, {10, 0}, {0, 5} }; 3294 const SkRect rect = {10, 10, 54, 54}; 3295 SkPaint ovalPaint; 3296 ovalPaint.setAntiAlias(true); 3297 SkPaint textPaint(ovalPaint); 3298 ovalPaint.setStyle(SkPaint::kStroke_Style); 3299 SkPaint arrowPaint(ovalPaint); 3300 SkPath arrowPath; 3301 arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true); 3302 arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0, 3303 SkPath1DPathEffect::kRotate_Style)); 3304 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) { 3305 for (unsigned start : { 0, 1, 2, 3 } ) { 3306 SkPath path; 3307 path.addOval(rect, direction, start); 3308 canvas->drawPath(path, ovalPaint); 3309 canvas->drawPath(path, arrowPaint); 3310 canvas->drawText(&"0123"[start], 1, rect.centerX(), rect.centerY() + 5, textPaint); 3311 canvas->translate(64, 0); 3312 } 3313 canvas->translate(-256, 72); 3314 canvas->drawString(SkPath::kCW_Direction == direction ? "clockwise" : "counterclockwise", 3315 128, 0, textPaint); 3316 } 3317} 3318## 3319 3320#SeeAlso SkCanvas::drawOval Direction Oval 3321 3322## 3323 3324# ------------------------------------------------------------------------------ 3325 3326#Method SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius, 3327 Direction dir = kCW_Direction) 3328#In Build 3329#Line # adds one Contour containing Circle ## 3330 3331Adds Circle centered at (x, y) of size radius to Path, appending kMove_Verb, 3332four kConic_Verb, and kClose_Verb. Circle begins at: #Formula # (x + radius, y) ##, continuing 3333clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction. 3334 3335Has no effect if radius is zero or negative. 3336 3337#Param x center of Circle ## 3338#Param y center of Circle ## 3339#Param radius distance from center to edge ## 3340#Param dir Direction to wind Circle ## 3341 3342#Return reference to Path ## 3343 3344#Example 3345void draw(SkCanvas* canvas) { 3346 SkPaint paint; 3347 paint.setAntiAlias(true); 3348 paint.setStyle(SkPaint::kStroke_Style); 3349 paint.setStrokeWidth(10); 3350 for (int size = 10; size < 300; size += 20) { 3351 SkPath path; 3352 path.addCircle(128, 128, size, SkPath::kCW_Direction); 3353 canvas->drawPath(path, paint); 3354 } 3355} 3356## 3357 3358#SeeAlso SkCanvas::drawCircle Direction Circle 3359 3360## 3361 3362# ------------------------------------------------------------------------------ 3363 3364#Method SkPath& addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) 3365#In Build 3366#Line # adds one Contour containing Arc ## 3367#Populate 3368 3369#Example 3370#Description 3371The middle row of the left and right columns draw differently from the entries 3372above and below because sweepAngle is outside of the range of +/-360, 3373and startAngle modulo 90 is not zero. 3374## 3375void draw(SkCanvas* canvas) { 3376 SkPaint paint; 3377 for (auto start : { 0, 90, 135, 180, 270 } ) { 3378 for (auto sweep : { -450.f, -180.f, -90.f, 90.f, 180.f, 360.1f } ) { 3379 SkPath path; 3380 path.addArc({10, 10, 35, 45}, start, sweep); 3381 canvas->drawPath(path, paint); 3382 canvas->translate(252 / 6, 0); 3383 } 3384 canvas->translate(-252, 255 / 5); 3385 } 3386} 3387## 3388 3389#SeeAlso Arc arcTo SkCanvas::drawArc 3390 3391## 3392 3393# ------------------------------------------------------------------------------ 3394 3395#Method SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 3396 Direction dir = kCW_Direction) 3397#In Build 3398#Line # adds one Contour containing Round_Rect with common corner radii ## 3399#Populate 3400 3401#Example 3402#Description 3403If either radius is zero, path contains Rect and is drawn red. 3404If sides are only radii, path contains Oval and is drawn blue. 3405All remaining path draws are convex, and are drawn in gray; no 3406paths constructed from addRoundRect are concave, so none are 3407drawn in green. 3408## 3409void draw(SkCanvas* canvas) { 3410 SkPaint paint; 3411 paint.setAntiAlias(true); 3412 for (auto xradius : { 0, 7, 13, 20 } ) { 3413 for (auto yradius : { 0, 9, 18, 40 } ) { 3414 SkPath path; 3415 path.addRoundRect({10, 10, 36, 46}, xradius, yradius); 3416 paint.setColor(path.isRect(nullptr) ? SK_ColorRED : path.isOval(nullptr) ? 3417 SK_ColorBLUE : path.isConvex() ? SK_ColorGRAY : SK_ColorGREEN); 3418 canvas->drawPath(path, paint); 3419 canvas->translate(64, 0); 3420 } 3421 canvas->translate(-256, 64); 3422 } 3423} 3424## 3425 3426#SeeAlso addRRect SkCanvas::drawRoundRect 3427 3428## 3429 3430# ------------------------------------------------------------------------------ 3431 3432#Method SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[], 3433 Direction dir = kCW_Direction) 3434 3435Appends Round_Rect to Path, creating a new closed Contour. Round_Rect has bounds 3436equal to rect; each corner is 90 degrees of an ellipse with radii from the 3437array. 3438 3439#Table 3440#Legend 3441# radii index # location ## 3442#Legend ## 3443# 0 # x-axis radius of top-left corner ## 3444# 1 # y-axis radius of top-left corner ## 3445# 2 # x-axis radius of top-right corner ## 3446# 3 # y-axis radius of top-right corner ## 3447# 4 # x-axis radius of bottom-right corner ## 3448# 5 # y-axis radius of bottom-right corner ## 3449# 6 # x-axis radius of bottom-left corner ## 3450# 7 # y-axis radius of bottom-left corner ## 3451#Table ## 3452 3453If dir is kCW_Direction, Round_Rect starts at top-left of the lower-left corner 3454and winds clockwise. If dir is kCCW_Direction, Round_Rect starts at the 3455bottom-left of the upper-left corner and winds counterclockwise. 3456 3457If both radii on any side of rect exceed its length, all radii are scaled 3458uniformly until the corners fit. If either radius of a corner is less than or 3459equal to zero, both are treated as zero. 3460 3461After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect. 3462 3463#Param rect bounds of Round_Rect ## 3464#Param radii array of 8 SkScalar values, a radius pair for each corner ## 3465#Param dir Direction to wind Round_Rect ## 3466 3467#Return reference to Path ## 3468 3469#Example 3470void draw(SkCanvas* canvas) { 3471 SkPaint paint; 3472 paint.setAntiAlias(true); 3473 SkScalar radii[] = { 80, 100, 0, 0, 40, 60, 0, 0 }; 3474 SkPath path; 3475 SkMatrix rotate90; 3476 rotate90.setRotate(90, 128, 128); 3477 for (int i = 0; i < 4; ++i) { 3478 path.addRoundRect({10, 10, 110, 110}, radii); 3479 path.transform(rotate90); 3480 } 3481 canvas->drawPath(path, paint); 3482} 3483## 3484 3485#SeeAlso addRRect SkCanvas::drawRoundRect 3486 3487## 3488 3489# ------------------------------------------------------------------------------ 3490 3491#Method SkPath& addRRect(const SkRRect& rrect, Direction dir = kCW_Direction) 3492#In Build 3493#Line # adds one Contour containing Round_Rect ## 3494#Populate 3495 3496#Example 3497void draw(SkCanvas* canvas) { 3498 SkPaint paint; 3499 paint.setAntiAlias(true); 3500 SkRRect rrect; 3501 SkVector radii[] = {{50, 50}, {0, 0}, {0, 0}, {50, 50}}; 3502 rrect.setRectRadii({10, 10, 110, 110}, radii); 3503 SkPath path; 3504 SkMatrix rotate90; 3505 rotate90.setRotate(90, 128, 128); 3506 for (int i = 0; i < 4; ++i) { 3507 path.addRRect(rrect); 3508 path.transform(rotate90); 3509 } 3510 canvas->drawPath(path, paint); 3511} 3512## 3513 3514#SeeAlso addRoundRect SkCanvas::drawRRect 3515 3516## 3517 3518# ------------------------------------------------------------------------------ 3519 3520#Method SkPath& addRRect(const SkRRect& rrect, Direction dir, unsigned start) 3521 3522Adds rrect to Path, creating a new closed Contour. If dir is kCW_Direction, rrect 3523winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise. 3524start determines the first point of rrect to add. 3525 3526#Table 3527#Legend 3528# start # location ## 3529#Legend ## 3530# 0 # right of top-left corner ## 3531# 1 # left of top-right corner ## 3532# 2 # bottom of top-right corner ## 3533# 3 # top of bottom-right corner ## 3534# 4 # left of bottom-right corner ## 3535# 5 # right of bottom-left corner ## 3536# 6 # top of bottom-left corner ## 3537# 7 # bottom of top-left corner ## 3538#Table ## 3539 3540After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect. 3541 3542#Param rrect bounds and radii of rounded rectangle ## 3543#Param dir Direction to wind Round_Rect ## 3544#Param start index of initial point of Round_Rect ## 3545 3546#Return reference to Path ## 3547 3548#Example 3549void draw(SkCanvas* canvas) { 3550 SkPaint paint; 3551 paint.setAntiAlias(true); 3552 SkRRect rrect; 3553 rrect.setRectXY({40, 40, 215, 215}, 50, 50); 3554 SkPath path; 3555 path.addRRect(rrect); 3556 canvas->drawPath(path, paint); 3557 for (int start = 0; start < 8; ++start) { 3558 SkPath textPath; 3559 textPath.addRRect(rrect, SkPath::kCW_Direction, start); 3560 SkPathMeasure pathMeasure(textPath, false); 3561 SkPoint position; 3562 SkVector tangent; 3563 if (!pathMeasure.getPosTan(0, &position, &tangent)) { 3564 continue; 3565 } 3566 SkRSXform rsxForm = SkRSXform::Make(tangent.fX, tangent.fY, 3567 position.fX + tangent.fY * 5, position.fY - tangent.fX * 5); 3568 canvas->drawTextRSXform(&"01234567"[start], 1, &rsxForm, nullptr, paint); 3569 } 3570} 3571## 3572 3573#SeeAlso addRoundRect SkCanvas::drawRRect 3574 3575## 3576 3577# ------------------------------------------------------------------------------ 3578 3579#Method SkPath& addPoly(const SkPoint pts[], int count, bool close) 3580#In Build 3581#Line # adds one Contour containing connected lines ## 3582#Populate 3583 3584#Example 3585void draw(SkCanvas* canvas) { 3586 SkPaint paint; 3587 paint.setStrokeWidth(15); 3588 paint.setStrokeCap(SkPaint::kRound_Cap); 3589 const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}}; 3590 for (bool close : { false, true } ) { 3591 SkPath path; 3592 path.addPoly(points, SK_ARRAY_COUNT(points), close); 3593 for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style, 3594 SkPaint::kStrokeAndFill_Style} ) { 3595 paint.setStyle(style); 3596 canvas->drawPath(path, paint); 3597 canvas->translate(85, 0); 3598 } 3599 canvas->translate(-255, 128); 3600 } 3601} 3602## 3603 3604#SeeAlso SkCanvas::drawPoints 3605 3606## 3607 3608#Method SkPath& addPoly(const std::initializer_list<SkPoint>& list, bool close) 3609#Populate 3610 3611#Example 3612void draw(SkCanvas* canvas) { 3613 SkPaint paint; 3614 paint.setStrokeWidth(15); 3615 paint.setStrokeCap(SkPaint::kRound_Cap); 3616 for (bool close : { false, true } ) { 3617 SkPath path; 3618 path.addPoly({{20, 20}, {70, 20}, {40, 90}}, close); 3619 for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style, 3620 SkPaint::kStrokeAndFill_Style} ) { 3621 paint.setStyle(style); 3622 canvas->drawPath(path, paint); 3623 canvas->translate(85, 0); 3624 } 3625 canvas->translate(-255, 128); 3626 } 3627} 3628## 3629 3630#SeeAlso SkCanvas::drawPoints 3631 3632## 3633 3634# ------------------------------------------------------------------------------ 3635 3636#Enum AddPathMode 3637#Line # sets addPath options ## 3638 3639#Code 3640 enum AddPathMode { 3641 kAppend_AddPathMode, 3642 kExtend_AddPathMode, 3643 }; 3644## 3645 3646AddPathMode chooses how addPath appends. Adding one Path to another can extend 3647the last Contour or start a new Contour. 3648 3649#Const kAppend_AddPathMode 3650#Line # appended to destination unaltered ## 3651 Path Verbs, Points, and Conic_Weights are appended to destination unaltered. 3652 Since Path Verb_Array begins with kMove_Verb if src is not empty, this 3653 starts a new Contour. 3654## 3655#Const kExtend_AddPathMode 3656#Line # add line if prior Contour is not closed ## 3657 If destination is closed or empty, start a new Contour. If destination 3658 is not empty, add Line from Last_Point to added Path first Point. Skip added 3659 Path initial kMove_Verb, then append remaining Verbs, Points, and Conic_Weights. 3660## 3661 3662#Example 3663#Description 3664test is built from path, open on the top row, and closed on the bottom row. 3665The left column uses kAppend_AddPathMode; the right uses kExtend_AddPathMode. 3666The top right composition is made up of one contour; the other three have two. 3667## 3668#Height 180 3669 SkPath path, path2; 3670 path.moveTo(20, 20); 3671 path.lineTo(20, 40); 3672 path.lineTo(40, 20); 3673 path2.moveTo(60, 60); 3674 path2.lineTo(80, 60); 3675 path2.lineTo(80, 40); 3676 SkPaint paint; 3677 paint.setStyle(SkPaint::kStroke_Style); 3678 for (int i = 0; i < 2; i++) { 3679 for (auto addPathMode : { SkPath::kAppend_AddPathMode, SkPath::kExtend_AddPathMode } ) { 3680 SkPath test(path); 3681 test.addPath(path2, addPathMode); 3682 canvas->drawPath(test, paint); 3683 canvas->translate(100, 0); 3684 } 3685 canvas->translate(-200, 100); 3686 path.close(); 3687 } 3688## 3689 3690#SeeAlso addPath reverseAddPath 3691 3692## 3693 3694# ------------------------------------------------------------------------------ 3695 3696#Method SkPath& addPath(const SkPath& src, SkScalar dx, SkScalar dy, 3697 AddPathMode mode = kAppend_AddPathMode) 3698#In Build 3699#Line # adds contents of Path ## 3700#Populate 3701 3702#Example 3703#Height 180 3704 SkPaint paint; 3705 paint.setTextSize(128); 3706 paint.setFakeBoldText(true); 3707 SkPath dest, text; 3708 paint.getTextPath("O", 1, 50, 120, &text); 3709 for (int i = 0; i < 3; i++) { 3710 dest.addPath(text, i * 20, i * 20); 3711 } 3712 Simplify(dest, &dest); 3713 paint.setStyle(SkPaint::kStroke_Style); 3714 paint.setStrokeWidth(3); 3715 canvas->drawPath(dest, paint); 3716## 3717 3718#SeeAlso AddPathMode offset reverseAddPath 3719 3720## 3721 3722# ------------------------------------------------------------------------------ 3723 3724#Method SkPath& addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode) 3725#Populate 3726 3727#Example 3728#Height 80 3729 SkPaint paint; 3730 paint.setStyle(SkPaint::kStroke_Style); 3731 SkPath dest, path; 3732 path.addOval({-80, 20, 0, 60}, SkPath::kCW_Direction, 1); 3733 for (int i = 0; i < 2; i++) { 3734 dest.addPath(path, SkPath::kExtend_AddPathMode); 3735 dest.offset(100, 0); 3736 } 3737 canvas->drawPath(dest, paint); 3738## 3739 3740#SeeAlso AddPathMode reverseAddPath 3741 3742## 3743 3744# ------------------------------------------------------------------------------ 3745 3746#Method SkPath& addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode) 3747#Populate 3748 3749#Example 3750#Height 160 3751 SkPaint paint; 3752 paint.setStyle(SkPaint::kStroke_Style); 3753 SkPath dest, path; 3754 path.addOval({20, 20, 200, 120}, SkPath::kCW_Direction, 1); 3755 for (int i = 0; i < 6; i++) { 3756 SkMatrix matrix; 3757 matrix.reset(); 3758 matrix.setPerspX(i / 400.f); 3759 dest.addPath(path, matrix); 3760 } 3761 canvas->drawPath(dest, paint); 3762## 3763 3764#SeeAlso AddPathMode transform offset reverseAddPath 3765 3766## 3767 3768# ------------------------------------------------------------------------------ 3769 3770#Method SkPath& reverseAddPath(const SkPath& src) 3771#In Build 3772#Line # adds contents of Path back to front ## 3773#Populate 3774 3775#Example 3776#Height 200 3777 SkPath path; 3778 path.moveTo(20, 20); 3779 path.lineTo(20, 40); 3780 path.lineTo(40, 20); 3781 SkPaint paint; 3782 paint.setStyle(SkPaint::kStroke_Style); 3783 for (int i = 0; i < 2; i++) { 3784 SkPath path2; 3785 path2.moveTo(60, 60); 3786 path2.lineTo(80, 60); 3787 path2.lineTo(80, 40); 3788 for (int j = 0; j < 2; j++) { 3789 SkPath test(path); 3790 test.reverseAddPath(path2); 3791 canvas->drawPath(test, paint); 3792 canvas->translate(100, 0); 3793 path2.close(); 3794 } 3795 canvas->translate(-200, 100); 3796 path.close(); 3797 } 3798## 3799 3800#SeeAlso AddPathMode transform offset addPath 3801 3802## 3803 3804# ------------------------------------------------------------------------------ 3805 3806#Method void offset(SkScalar dx, SkScalar dy, SkPath* dst) const 3807#In Transform 3808#Line # translates Point_Array ## 3809#Populate 3810 3811#Example 3812#Height 60 3813 SkPath pattern; 3814 pattern.moveTo(20, 20); 3815 pattern.lineTo(20, 40); 3816 pattern.lineTo(40, 20); 3817 SkPaint paint; 3818 paint.setStyle(SkPaint::kStroke_Style); 3819 for (int i = 0; i < 10; i++) { 3820 SkPath path; 3821 pattern.offset(20 * i, 0, &path); 3822 canvas->drawPath(path, paint); 3823 } 3824## 3825 3826#SeeAlso addPath transform 3827 3828## 3829 3830# ------------------------------------------------------------------------------ 3831#Subtopic Transform 3832#Line # modify all points ## 3833## 3834 3835#Method void offset(SkScalar dx, SkScalar dy) 3836#In Transform 3837#Populate 3838 3839#Example 3840#Height 60 3841 SkPath path; 3842 path.moveTo(20, 20); 3843 path.lineTo(20, 40); 3844 path.lineTo(40, 20); 3845 SkPaint paint; 3846 paint.setStyle(SkPaint::kStroke_Style); 3847 for (int i = 0; i < 10; i++) { 3848 canvas->drawPath(path, paint); 3849 path.offset(20, 0); 3850 } 3851## 3852 3853#SeeAlso addPath transform SkCanvas::translate() 3854 3855## 3856 3857# ------------------------------------------------------------------------------ 3858 3859#Method void transform(const SkMatrix& matrix, SkPath* dst) const 3860#In Transform 3861#Line # applies Matrix to Point_Array and Weights ## 3862#Populate 3863 3864#Example 3865#Height 200 3866 SkPath pattern; 3867 pattern.moveTo(100, 100); 3868 pattern.lineTo(100, 20); 3869 pattern.lineTo(20, 100); 3870 SkPaint paint; 3871 paint.setStyle(SkPaint::kStroke_Style); 3872 for (int i = 0; i < 10; i++) { 3873 SkPath path; 3874 SkMatrix matrix; 3875 matrix.setRotate(36 * i, 100, 100); 3876 pattern.transform(matrix, &path); 3877 canvas->drawPath(path, paint); 3878 } 3879## 3880 3881#SeeAlso addPath offset SkCanvas::concat() SkMatrix 3882 3883## 3884 3885# ------------------------------------------------------------------------------ 3886 3887#Method void transform(const SkMatrix& matrix) 3888#Populate 3889 3890#Example 3891#Height 200 3892 SkPath path; 3893 path.moveTo(100, 100); 3894 path.quadTo(100, 20, 20, 100); 3895 SkPaint paint; 3896 paint.setStyle(SkPaint::kStroke_Style); 3897 for (int i = 0; i < 10; i++) { 3898 SkMatrix matrix; 3899 matrix.setRotate(36, 100, 100); 3900 path.transform(matrix); 3901 canvas->drawPath(path, paint); 3902 } 3903## 3904 3905#SeeAlso addPath offset SkCanvas::concat() SkMatrix 3906 3907## 3908 3909# ------------------------------------------------------------------------------ 3910 3911#Subtopic Last_Point 3912#Line # final Point in Contour ## 3913 3914Path is defined cumulatively, often by adding a segment to the end of last 3915Contour. Last_Point of Contour is shared as first Point of added Line or Curve. 3916Last_Point can be read and written directly with getLastPt and setLastPt. 3917 3918#Method bool getLastPt(SkPoint* lastPt) const 3919#In Property 3920#In Last_Point 3921#Line # returns Last_Point ## 3922#Populate 3923 3924#Example 3925 SkPath path; 3926 path.moveTo(100, 100); 3927 path.quadTo(100, 20, 20, 100); 3928 SkMatrix matrix; 3929 matrix.setRotate(36, 100, 100); 3930 path.transform(matrix); 3931 SkPoint last; 3932 path.getLastPt(&last); 3933 SkDebugf("last point: %g, %g\n", last.fX, last.fY); 3934 #StdOut 3935 last point: 35.2786, 52.9772 3936 ## 3937 ## 3938 3939 #SeeAlso setLastPt 3940 3941## 3942 3943#Method void setLastPt(SkScalar x, SkScalar y) 3944#In Utility 3945#In Last_Point 3946#Line # replaces Last_Point ## 3947#Populate 3948 3949#Example 3950 #Height 128 3951 SkPaint paint; 3952 paint.setTextSize(128); 3953 SkPath path; 3954 paint.getTextPath("@", 1, 60, 100, &path); 3955 path.setLastPt(20, 120); 3956 canvas->drawPath(path, paint); 3957 ## 3958 3959 #SeeAlso getLastPt 3960 3961## 3962 3963#Method void setLastPt(const SkPoint& p) 3964#Populate 3965 3966#Example 3967 #Height 128 3968 SkPaint paint; 3969 paint.setTextSize(128); 3970 SkPath path, path2; 3971 paint.getTextPath("A", 1, 60, 100, &path); 3972 paint.getTextPath("Z", 1, 60, 100, &path2); 3973 SkPoint pt, pt2; 3974 path.getLastPt(&pt); 3975 path2.getLastPt(&pt2); 3976 path.setLastPt(pt2); 3977 path2.setLastPt(pt); 3978 canvas->drawPath(path, paint); 3979 canvas->drawPath(path2, paint); 3980 ## 3981 3982 #SeeAlso getLastPt 3983 3984## 3985 3986#Subtopic Last_Point ## 3987 3988# ------------------------------------------------------------------------------ 3989 3990#Enum SegmentMask 3991#Line # returns Verb types in Path ## 3992 3993#Code 3994 enum SegmentMask { 3995 kLine_SegmentMask = 1 << 0, 3996 kQuad_SegmentMask = 1 << 1, 3997 kConic_SegmentMask = 1 << 2, 3998 kCubic_SegmentMask = 1 << 3, 3999 }; 4000## 4001 4002SegmentMask constants correspond to each drawing Verb type in Path; for 4003instance, if Path only contains Lines, only the kLine_SegmentMask bit is set. 4004 4005#Bug 6785 4006#Const kLine_SegmentMask 1 4007#Line # contains one or more Lines ## 4008Set if Verb_Array contains kLine_Verb. 4009## 4010#Const kQuad_SegmentMask 2 4011#Line # contains one or more Quads ## 4012Set if Verb_Array contains kQuad_Verb. Note that conicTo may add a Quad. 4013## 4014#Const kConic_SegmentMask 4 4015#Line # contains one or more Conics ## 4016Set if Verb_Array contains kConic_Verb. 4017## 4018#Const kCubic_SegmentMask 8 4019#Line # contains one or more Cubics ## 4020Set if Verb_Array contains kCubic_Verb. 4021## 4022 4023#Example 4024#Description 4025When conicTo has a weight of one, Quad is added to Path. 4026## 4027 SkPath path; 4028 path.conicTo(10, 10, 20, 30, 1); 4029 SkDebugf("Path kConic_SegmentMask is %s\n", path.getSegmentMasks() & 4030 SkPath::kConic_SegmentMask ? "set" : "clear"); 4031 SkDebugf("Path kQuad_SegmentMask is %s\n", path.getSegmentMasks() & 4032 SkPath::kQuad_SegmentMask ? "set" : "clear"); 4033#StdOut 4034Path kConic_SegmentMask is clear 4035Path kQuad_SegmentMask is set 4036## 4037## 4038 4039#SeeAlso getSegmentMasks Verb 4040 4041## 4042 4043# ------------------------------------------------------------------------------ 4044 4045#Method uint32_t getSegmentMasks() const 4046#In Utility 4047#In Property 4048#Line # returns types in Verb_Array ## 4049#Populate 4050 4051#Example 4052SkPath path; 4053path.quadTo(20, 30, 40, 50); 4054path.close(); 4055const char* masks[] = { "line", "quad", "conic", "cubic" }; 4056int index = 0; 4057for (auto mask : { SkPath::kLine_SegmentMask, SkPath::kQuad_SegmentMask, 4058 SkPath::kConic_SegmentMask, SkPath::kCubic_SegmentMask } ) { 4059 if (mask & path.getSegmentMasks()) { 4060 SkDebugf("mask %s set\n", masks[index]); 4061 } 4062 ++index; 4063} 4064#StdOut 4065mask quad set 4066## 4067## 4068 4069#SeeAlso getSegmentMasks Verb 4070 4071## 4072 4073# ------------------------------------------------------------------------------ 4074 4075#Method bool contains(SkScalar x, SkScalar y) const 4076#In Property 4077#Line # returns if Point is in fill area ## 4078Returns true if the point (x, y) is contained by Path, taking into 4079account FillType. 4080 4081#Table 4082#Legend 4083# FillType # contains() returns true if Point is enclosed by ## 4084## 4085# kWinding_FillType # a non-zero sum of Contour Directions. ## 4086# kEvenOdd_FillType # an odd number of Contours. ## 4087# kInverseWinding_FillType # a zero sum of Contour Directions. ## 4088# kInverseEvenOdd_FillType # and even number of Contours. ## 4089## 4090 4091#Param x x-axis value of containment test ## 4092#Param y y-axis value of containment test ## 4093 4094#Return true if Point is in Path ## 4095 4096#Example 4097SkPath path; 4098SkPaint paint; 4099paint.setTextSize(256); 4100paint.getTextPath("&", 1, 30, 220, &path); 4101for (int y = 2; y < 256; y += 9) { 4102 for (int x = 2; x < 256; x += 9) { 4103 int coverage = 0; 4104 for (int iy = -4; iy <= 4; iy += 2) { 4105 for (int ix = -4; ix <= 4; ix += 2) { 4106 coverage += path.contains(x + ix, y + iy); 4107 } 4108 } 4109 paint.setColor(SkColorSetARGB(0x5f, 0xff * coverage / 25, 0, 0xff * (25 - coverage) / 25)); 4110 canvas->drawCircle(x, y, 8, paint); 4111 } 4112} 4113## 4114 4115#SeeAlso conservativelyContainsRect Fill_Type Op 4116 4117## 4118 4119# ------------------------------------------------------------------------------ 4120 4121#Method void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const 4122#In Utility 4123#Line # sends text representation to stream ## 4124#Populate 4125 4126#Example 4127 SkPath path; 4128 path.quadTo(20, 30, 40, 50); 4129 for (bool forceClose : { false, true } ) { 4130 for (bool dumpAsHex : { false, true } ) { 4131 path.dump(nullptr, forceClose, dumpAsHex); 4132 SkDebugf("\n"); 4133 } 4134 } 4135#StdOut 4136path.setFillType(SkPath::kWinding_FillType); 4137path.moveTo(0, 0); 4138path.quadTo(20, 30, 40, 50); 4139 4140path.setFillType(SkPath::kWinding_FillType); 4141path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 4142path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000)); // 20, 30, 40, 50 4143 4144path.setFillType(SkPath::kWinding_FillType); 4145path.moveTo(0, 0); 4146path.quadTo(20, 30, 40, 50); 4147path.lineTo(0, 0); 4148path.close(); 4149 4150path.setFillType(SkPath::kWinding_FillType); 4151path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 4152path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000)); // 20, 30, 40, 50 4153path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 4154path.close(); 4155## 4156## 4157 4158#SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump() 4159 4160## 4161 4162# ------------------------------------------------------------------------------ 4163 4164#Method void dump() const 4165#Populate 4166 4167#Example 4168SkPath path, copy; 4169path.lineTo(6.f / 7, 2.f / 3); 4170path.dump(); 4171copy.setFillType(SkPath::kWinding_FillType); 4172copy.moveTo(0, 0); 4173copy.lineTo(0.857143f, 0.666667f); 4174SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); 4175#StdOut 4176path.setFillType(SkPath::kWinding_FillType); 4177path.moveTo(0, 0); 4178path.lineTo(0.857143f, 0.666667f); 4179path is not equal to copy 4180## 4181## 4182 4183#SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump() writeToMemory 4184 4185## 4186 4187# ------------------------------------------------------------------------------ 4188 4189#Method void dumpHex() const 4190#In Utility 4191#Line # sends text representation using hexadecimal to standard output ## 4192Writes text representation of Path to standard output. The representation may be 4193directly compiled as C++ code. Floating point values are written 4194in hexadecimal to preserve their exact bit pattern. The output reconstructs the 4195original Path. 4196 4197Use instead of dump() when submitting 4198#A bug reports against Skia # https://bug.skia.org ## 4199. 4200 4201#Example 4202SkPath path, copy; 4203path.lineTo(6.f / 7, 2.f / 3); 4204path.dumpHex(); 4205copy.setFillType(SkPath::kWinding_FillType); 4206copy.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 4207copy.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab)); // 0.857143f, 0.666667f 4208SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); 4209#StdOut 4210path.setFillType(SkPath::kWinding_FillType); 4211path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0 4212path.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab)); // 0.857143f, 0.666667f 4213path is equal to copy 4214## 4215## 4216 4217#SeeAlso dump SkRect::dumpHex SkRRect::dumpHex writeToMemory 4218 4219## 4220 4221# ------------------------------------------------------------------------------ 4222 4223#Method size_t writeToMemory(void* buffer) const 4224#In Utility 4225#Line # copies data to buffer ## 4226#Populate 4227 4228#Example 4229void draw(SkCanvas* canvas) { 4230 SkPath path, copy; 4231 path.lineTo(6.f / 7, 2.f / 3); 4232 size_t size = path.writeToMemory(nullptr); 4233 SkTDArray<char> storage; 4234 storage.setCount(size); 4235 path.writeToMemory(storage.begin()); 4236 copy.readFromMemory(storage.begin(), size); 4237 SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); 4238} 4239#StdOut 4240path is equal to copy 4241## 4242## 4243 4244#SeeAlso serialize readFromMemory dump dumpHex 4245 4246## 4247 4248#Method sk_sp<SkData> serialize() const 4249#In Utility 4250#Line # copies data to buffer ## 4251#Populate 4252 4253#Example 4254void draw(SkCanvas* canvas) { 4255 SkPath path, copy; 4256 path.lineTo(6.f / 7, 2.f / 3); 4257 sk_sp<SkData> data = path.serialize(); 4258 copy.readFromMemory(data->data(), data->size()); 4259 SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not "); 4260} 4261#StdOut 4262path is equal to copy 4263## 4264## 4265 4266#SeeAlso writeToMemory readFromMemory dump dumpHex 4267## 4268 4269# ------------------------------------------------------------------------------ 4270 4271#Method size_t readFromMemory(const void* buffer, size_t length) 4272#In Utility 4273#Line # initializes from buffer ## 4274#Populate 4275 4276#Example 4277void draw(SkCanvas* canvas) { 4278 SkPath path, copy; 4279 path.lineTo(6.f / 7, 2.f / 3); 4280 size_t size = path.writeToMemory(nullptr); 4281 SkTDArray<char> storage; 4282 storage.setCount(size); 4283 path.writeToMemory(storage.begin()); 4284 size_t wrongSize = size - 4; 4285 size_t bytesRead = copy.readFromMemory(storage.begin(), wrongSize); 4286 SkDebugf("length = %u; returned by readFromMemory = %u\n", wrongSize, bytesRead); 4287 size_t largerSize = size + 4; 4288 bytesRead = copy.readFromMemory(storage.begin(), largerSize); 4289 SkDebugf("length = %u; returned by readFromMemory = %u\n", largerSize, bytesRead); 4290} 4291#StdOut 4292length = 32; returned by readFromMemory = 0 4293length = 40; returned by readFromMemory = 36 4294## 4295## 4296 4297#SeeAlso writeToMemory 4298 4299## 4300 4301# ------------------------------------------------------------------------------ 4302#Subtopic Generation_ID 4303#Alias Generation_IDs ## 4304#Line # value reflecting contents change ## 4305Generation_ID provides a quick way to check if Verb_Array, Point_Array, or 4306Conic_Weight has changed. Generation_ID is not a hash; identical Paths will 4307not necessarily have matching Generation_IDs. 4308 4309Empty Paths have a Generation_ID of one. 4310 4311#Method uint32_t getGenerationID() const 4312 4313#In Generation_ID 4314#Line # returns unique ID ## 4315#Populate 4316 4317#Example 4318SkPath path; 4319SkDebugf("empty genID = %u\n", path.getGenerationID()); 4320path.lineTo(1, 2); 4321SkDebugf("1st lineTo genID = %u\n", path.getGenerationID()); 4322path.rewind(); 4323SkDebugf("empty genID = %u\n", path.getGenerationID()); 4324path.lineTo(1, 2); 4325SkDebugf("2nd lineTo genID = %u\n", path.getGenerationID()); 4326#StdOut 4327empty genID = 1 43281st lineTo genID = 2 4329empty genID = 1 43302nd lineTo genID = 3 4331## 4332## 4333 4334#SeeAlso operator==(const SkPath& a, const SkPath& b) 4335 4336## 4337 4338#Subtopic ## 4339 4340# ------------------------------------------------------------------------------ 4341 4342#Method bool isValid() const 4343#In Property 4344#In Utility 4345#Line # returns if data is internally consistent ## 4346#Populate 4347 4348#NoExample 4349 ## 4350 4351## 4352 4353# ------------------------------------------------------------------------------ 4354 4355#Class Iter 4356#Line # data iterator ## 4357 4358#Code 4359#Populate 4360## 4361 4362Iterates through Verb_Array, and associated Point_Array and Conic_Weight. 4363Provides options to treat open Contours as closed, and to ignore 4364degenerate data. 4365 4366#Example 4367#Height 128 4368#Description 4369Ignoring the actual Verbs and replacing them with Quads rounds the 4370path of the glyph. 4371## 4372void draw(SkCanvas* canvas) { 4373 SkPaint paint; 4374 paint.setAntiAlias(true); 4375 paint.setTextSize(256); 4376 SkPath asterisk, path; 4377 paint.getTextPath("*", 1, 50, 192, &asterisk); 4378 SkPath::Iter iter(asterisk, true); 4379 SkPoint start[4], pts[4]; 4380 iter.next(start); // skip moveTo 4381 iter.next(start); // first quadTo 4382 path.moveTo((start[0] + start[1]) * 0.5f); 4383 while (SkPath::kClose_Verb != iter.next(pts)) { 4384 path.quadTo(pts[0], (pts[0] + pts[1]) * 0.5f); 4385 } 4386 path.quadTo(start[0], (start[0] + start[1]) * 0.5f); 4387 canvas->drawPath(path, paint); 4388} 4389## 4390 4391#SeeAlso RawIter 4392 4393#Method Iter() 4394#Line # constructs Path iterator ## 4395#Populate 4396 4397#Example 4398void draw(SkCanvas* canvas) { 4399 SkPath::Iter iter; 4400 SkPoint points[4]; 4401 SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not "); 4402 SkPath path; 4403 iter.setPath(path, false); 4404 SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not "); 4405} 4406#StdOut 4407iter is done 4408iter is done 4409## 4410## 4411 4412#SeeAlso setPath 4413 4414## 4415 4416#Method Iter(const SkPath& path, bool forceClose) 4417#Line # constructs Path iterator ## 4418#Populate 4419 4420#Example 4421void draw(SkCanvas* canvas) { 4422 auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void { 4423 SkDebugf("%s:\n", prefix); 4424 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; 4425 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 4426 SkPath::Verb verb; 4427 do { 4428 SkPoint points[4]; 4429 verb = iter.next(points); 4430 SkDebugf("k%s_Verb ", verbStr[(int) verb]); 4431 for (int i = 0; i < pointCount[(int) verb]; ++i) { 4432 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); 4433 } 4434 if (SkPath::kConic_Verb == verb) { 4435 SkDebugf("weight = %g", iter.conicWeight()); 4436 } 4437 SkDebugf("\n"); 4438 } while (SkPath::kDone_Verb != verb); 4439 SkDebugf("\n"); 4440 }; 4441 4442 SkPath path; 4443 path.quadTo(10, 20, 30, 40); 4444 SkPath::Iter openIter(path, false); 4445 debugster("open", openIter); 4446 SkPath::Iter closedIter(path, true); 4447 debugster("closed", closedIter); 4448} 4449#StdOut 4450open: 4451kMove_Verb {0, 0}, 4452kQuad_Verb {0, 0}, {10, 20}, {30, 40}, 4453kDone_Verb 4454 4455closed: 4456kMove_Verb {0, 0}, 4457kQuad_Verb {0, 0}, {10, 20}, {30, 40}, 4458kLine_Verb {30, 40}, {0, 0}, 4459kClose_Verb {0, 0}, 4460kDone_Verb 4461## 4462## 4463 4464#SeeAlso setPath 4465 4466## 4467 4468#Method void setPath(const SkPath& path, bool forceClose) 4469#Line # resets Iter to Path ## 4470#Populate 4471 4472#Example 4473void draw(SkCanvas* canvas) { 4474 auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void { 4475 SkDebugf("%s:\n", prefix); 4476 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; 4477 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 4478 SkPath::Verb verb; 4479 do { 4480 SkPoint points[4]; 4481 verb = iter.next(points); 4482 SkDebugf("k%s_Verb ", verbStr[(int) verb]); 4483 for (int i = 0; i < pointCount[(int) verb]; ++i) { 4484 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY); 4485 } 4486 if (SkPath::kConic_Verb == verb) { 4487 SkDebugf("weight = %g", iter.conicWeight()); 4488 } 4489 SkDebugf("\n"); 4490 } while (SkPath::kDone_Verb != verb); 4491 SkDebugf("\n"); 4492 }; 4493 4494 SkPath path; 4495 path.quadTo(10, 20, 30, 40); 4496 SkPath::Iter iter(path, false); 4497 debugster("quad open", iter); 4498 SkPath path2; 4499 path2.conicTo(1, 2, 3, 4, .5f); 4500 iter.setPath(path2, true); 4501 debugster("conic closed", iter); 4502} 4503#StdOut 4504quad open: 4505kMove_Verb {0, 0}, 4506kQuad_Verb {0, 0}, {10, 20}, {30, 40}, 4507kDone_Verb 4508 4509conic closed: 4510kMove_Verb {0, 0}, 4511kConic_Verb {0, 0}, {1, 2}, {3, 4}, weight = 0.5 4512kLine_Verb {3, 4}, {0, 0}, 4513kClose_Verb {0, 0}, 4514kDone_Verb 4515## 4516## 4517 4518#SeeAlso Iter(const SkPath& path, bool forceClose) 4519 4520## 4521 4522#Method Verb next(SkPoint pts[4], bool doConsumeDegenerates = true, bool exact = false) 4523#Line # returns next Verb ## 4524#Populate 4525 4526#Example 4527#Description 4528skip degenerate skips the first in a kMove_Verb pair, the kMove_Verb 4529followed by the kClose_Verb, the zero length Line and the very small Line. 4530 4531skip degenerate if exact skips the same as skip degenerate, but shows 4532the very small Line. 4533 4534skip none shows all of the Verbs and Points in Path. 4535## 4536void draw(SkCanvas* canvas) { 4537 auto debugster = [](const char* prefix, const SkPath& path, bool degen, bool exact) -> void { 4538 SkPath::Iter iter(path, false); 4539 SkDebugf("%s:\n", prefix); 4540 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; 4541 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 4542 SkPath::Verb verb; 4543 do { 4544 SkPoint points[4]; 4545 verb = iter.next(points, degen, exact); 4546 SkDebugf("k%s_Verb ", verbStr[(int) verb]); 4547 for (int i = 0; i < pointCount[(int) verb]; ++i) { 4548 SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY); 4549 } 4550 SkDebugf("\n"); 4551 } while (SkPath::kDone_Verb != verb); 4552 SkDebugf("\n"); 4553 }; 4554 4555 SkPath path; 4556 path.moveTo(10, 10); 4557 path.moveTo(20, 20); 4558 path.quadTo(10, 20, 30, 40); 4559 path.moveTo(1, 1); 4560 path.close(); 4561 path.moveTo(30, 30); 4562 path.lineTo(30, 30); 4563 path.moveTo(30, 30); 4564 path.lineTo(30.00001f, 30); 4565 debugster("skip degenerate", path, true, false); 4566 debugster("skip degenerate if exact", path, true, true); 4567 debugster("skip none", path, false, false); 4568} 4569#StdOut 4570skip degenerate: 4571kMove_Verb {20, 20}, 4572kQuad_Verb {20, 20}, {10, 20}, {30, 40}, 4573kDone_Verb 4574 4575skip degenerate if exact: 4576kMove_Verb {20, 20}, 4577kQuad_Verb {20, 20}, {10, 20}, {30, 40}, 4578kMove_Verb {30, 30}, 4579kLine_Verb {30, 30}, {30.00001, 30}, 4580kDone_Verb 4581 4582skip none: 4583kMove_Verb {10, 10}, 4584kMove_Verb {20, 20}, 4585kQuad_Verb {20, 20}, {10, 20}, {30, 40}, 4586kMove_Verb {1, 1}, 4587kClose_Verb {1, 1}, 4588kMove_Verb {30, 30}, 4589kLine_Verb {30, 30}, {30, 30}, 4590kMove_Verb {30, 30}, 4591kLine_Verb {30, 30}, {30.00001, 30}, 4592kDone_Verb 4593## 4594## 4595 4596#SeeAlso Verb IsLineDegenerate IsCubicDegenerate IsQuadDegenerate 4597 4598## 4599 4600#Method SkScalar conicWeight() const 4601#Line # returns Conic_Weight ## 4602#Populate 4603 4604#Example 4605 void draw(SkCanvas* canvas) { 4606 SkPath path; 4607 path.conicTo(1, 2, 3, 4, .5f); 4608 SkPath::Iter iter(path, false); 4609 SkPoint p[4]; 4610 SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not "); 4611 SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not "); 4612 SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY, 4613 p[2].fX, p[2].fY); 4614 SkDebugf("conic weight: %g\n", iter.conicWeight()); 4615 } 4616 #StdOut 4617first verb is move 4618next verb is conic 4619conic points: {0,0}, {1,2}, {3,4} 4620conic weight: 0.5 4621 ## 4622 ## 4623 4624 #SeeAlso Conic_Weight 4625 4626## 4627 4628#Method bool isCloseLine() const 4629#Line # returns if Line was generated by kClose_Verb ## 4630#Populate 4631 4632#Example 4633void draw(SkCanvas* canvas) { 4634 SkPath path; 4635 path.moveTo(6, 7); 4636 path.conicTo(1, 2, 3, 4, .5f); 4637 path.close(); 4638 SkPath::Iter iter(path, false); 4639 SkPoint p[4]; 4640 SkDebugf("1st verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not "); 4641 SkDebugf("moveTo point: {%g,%g}\n", p[0].fX, p[0].fY); 4642 SkDebugf("2nd verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not "); 4643 SkDebugf("3rd verb is " "%s" "line\n", SkPath::kLine_Verb == iter.next(p) ? "" : "not "); 4644 SkDebugf("line points: {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY); 4645 SkDebugf("line " "%s" "generated by close\n", iter.isCloseLine() ? "" : "not "); 4646 SkDebugf("4th verb is " "%s" "close\n", SkPath::kClose_Verb == iter.next(p) ? "" : "not "); 4647} 4648 #StdOut 46491st verb is move 4650moveTo point: {6,7} 46512nd verb is conic 46523rd verb is line 4653line points: {3,4}, {6,7} 4654line generated by close 46554th verb is close 4656 ## 4657 ## 4658 4659 #SeeAlso close() 4660## 4661 4662#Method bool isClosedContour() const 4663#Line # returns if Contour has kClose_Verb ## 4664#Populate 4665 4666#Example 4667void draw(SkCanvas* canvas) { 4668 for (bool forceClose : { false, true } ) { 4669 SkPath path; 4670 path.conicTo(1, 2, 3, 4, .5f); 4671 SkPath::Iter iter(path, forceClose); 4672 SkDebugf("without close(), forceClose is %s: isClosedContour returns %s\n", 4673 forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false"); 4674 path.close(); 4675 iter.setPath(path, forceClose); 4676 SkDebugf("with close(), forceClose is %s: isClosedContour returns %s\n", 4677 forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false"); 4678 } 4679} 4680#StdOut 4681without close(), forceClose is false: isClosedContour returns false 4682with close(), forceClose is false: isClosedContour returns true 4683without close(), forceClose is true : isClosedContour returns true 4684with close(), forceClose is true : isClosedContour returns true 4685## 4686## 4687 4688#SeeAlso Iter(const SkPath& path, bool forceClose) 4689 4690## 4691 4692#Class Iter ## 4693 4694#Class RawIter 4695#Line # raw data iterator ## 4696 4697#Code 4698#Populate 4699## 4700 4701Iterates through Verb_Array, and associated Point_Array and Conic_Weight. 4702Verb_Array, Point_Array, and Conic_Weight are returned unaltered. 4703 4704 4705 #Method RawIter() 4706 #Line # constructs empty Path iterator ## 4707#Populate 4708 4709#NoExample 4710 ## 4711 ## 4712 4713 #Method RawIter(const SkPath& path) 4714 #Line # constructs with Path to iterate over ## 4715#Populate 4716 4717#NoExample 4718 ## 4719 ## 4720 4721 #Method void setPath(const SkPath& path) 4722 #Line # sets Path to iterate over ## 4723#Populate 4724 4725#NoExample 4726 ## 4727 ## 4728 4729 #Method Verb next(SkPoint pts[4]) 4730 #Line # returns next Verb and associated Points ## 4731#Populate 4732 4733#Example 4734 void draw(SkCanvas* canvas) { 4735 SkPath path; 4736 path.moveTo(50, 60); 4737 path.quadTo(10, 20, 30, 40); 4738 path.close(); 4739 path.lineTo(30, 30); 4740 path.conicTo(1, 2, 3, 4, .5f); 4741 path.cubicTo(-1, -2, -3, -4, -5, -6); 4742 SkPath::RawIter iter(path); 4743 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; 4744 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 }; 4745 SkPath::Verb verb; 4746 do { 4747 SkPoint points[4]; 4748 verb = iter.next(points); 4749 SkDebugf("k%s_Verb ", verbStr[(int) verb]); 4750 for (int i = 0; i < pointCount[(int) verb]; ++i) { 4751 SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY); 4752 } 4753 if (SkPath::kConic_Verb == verb) { 4754 SkDebugf("weight = %g", iter.conicWeight()); 4755 } 4756 SkDebugf("\n"); 4757 } while (SkPath::kDone_Verb != verb); 4758 } 4759 #StdOut 4760 kMove_Verb {50, 60}, 4761 kQuad_Verb {50, 60}, {10, 20}, {30, 40}, 4762 kClose_Verb {50, 60}, 4763 kMove_Verb {50, 60}, 4764 kLine_Verb {50, 60}, {30, 30}, 4765 kConic_Verb {30, 30}, {1, 2}, {3, 4}, weight = 0.5 4766 kCubic_Verb {3, 4}, {-1, -2}, {-3, -4}, {-5, -6}, 4767 kDone_Verb 4768 ## 4769 ## 4770 4771 #SeeAlso peek() 4772 4773 ## 4774 4775 #Method Verb peek() const 4776 #Line # returns next Verb ## 4777 #Populate 4778 4779 #Example 4780 SkPath path; 4781 path.quadTo(10, 20, 30, 40); 4782 path.conicTo(1, 2, 3, 4, .5f); 4783 path.cubicTo(1, 2, 3, 4, .5, 6); 4784 SkPath::RawIter iter(path); 4785 SkPath::Verb verb, peek = iter.peek(); 4786 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" }; 4787 do { 4788 SkPoint points[4]; 4789 verb = iter.next(points); 4790 SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]); 4791 peek = iter.peek(); 4792 } while (SkPath::kDone_Verb != verb); 4793 SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]); 4794 #StdOut 4795 #Volatile 4796 peek Move == verb Move 4797 peek Quad == verb Quad 4798 peek Conic == verb Conic 4799 peek Cubic == verb Cubic 4800 peek Done == verb Done 4801 peek Done == verb Done 4802 ## 4803 ## 4804 4805 #Bug 6832 4806 # StdOut is not really volatile, it just produces the wrong result. 4807 # A simple fix changes the output of hairlines and needs to be 4808 # investigated to see if the change is correct or not. 4809 # see change 21340 (abandoned for now) 4810 4811 #SeeAlso next 4812 4813 ## 4814 4815 #Method SkScalar conicWeight() const 4816 #Line # returns Conic_Weight ## 4817#Populate 4818 4819#Example 4820 void draw(SkCanvas* canvas) { 4821 SkPath path; 4822 path.conicTo(1, 2, 3, 4, .5f); 4823 SkPath::RawIter iter(path); 4824 SkPoint p[4]; 4825 SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not "); 4826 SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not "); 4827 SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY, 4828 p[2].fX, p[2].fY); 4829 SkDebugf("conic weight: %g\n", iter.conicWeight()); 4830 } 4831 #StdOut 4832 first verb is move 4833 next verb is conic 4834 conic points: {0,0}, {1,2}, {3,4} 4835 conic weight: 0.5 4836 ## 4837 ## 4838 4839 #SeeAlso Conic_Weight 4840 4841 ## 4842 4843#Class RawIter ## 4844 4845#Class SkPath ## 4846 4847#Topic Path ## 4848