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