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