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