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