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