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