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