blob: abb775831e20de041533fabb067ccdf80960628f [file] [log] [blame]
Cary Clark73fa9722017-08-29 17:36:51 -04001#Topic Path
Cary Clark137b8742018-05-30 09:21:49 -04002#Alias Path_Reference ##
3#Alias Paths ##
Cary Clark73fa9722017-08-29 17:36:51 -04004
Cary Clark73fa9722017-08-29 17:36:51 -04005#Class SkPath
6
Cary Clark61313f32018-10-08 14:57:48 -04007#Code
8#Populate
9##
10
Cary Clark73fa9722017-08-29 17:36:51 -040011Paths contain geometry. Paths may be empty, or contain one or more Verbs that
Cary Clarka560c472017-11-27 10:44:06 -050012outline a figure. Path always starts with a move verb to a Cartesian_Coordinate,
13and may be followed by additional verbs that add lines or curves.
Cary Clark73fa9722017-08-29 17:36:51 -040014Adding a close verb makes the geometry into a continuous loop, a closed contour.
Cary Clarkce101242017-09-01 15:51:02 -040015Paths may contain any number of contours, each beginning with a move verb.
Cary Clark73fa9722017-08-29 17:36:51 -040016
17Path contours may contain only a move verb, or may also contain lines,
Cary Clarkce101242017-09-01 15:51:02 -040018Quadratic_Beziers, Conics, and Cubic_Beziers. Path contours may be open or
Cary Clark73fa9722017-08-29 17:36:51 -040019closed.
20
21When used to draw a filled area, Path describes whether the fill is inside or
22outside the geometry. Path also describes the winding rule used to fill
23overlapping contours.
24
25Internally, Path lazily computes metrics likes bounds and convexity. Call
Cary Clark682c58d2018-05-16 07:07:07 -040026SkPath::updateBoundsCache to make Path thread safe.
27
Cary Clark8032b982017-07-28 11:04:54 -040028#Subtopic Verb
Cary Clark137b8742018-05-30 09:21:49 -040029#Alias Verbs ##
Cary Clark08895c42018-02-01 09:37:32 -050030#Line # line and curve type ##
Cary Clark73fa9722017-08-29 17:36:51 -040031#Enum Verb
Cary Clark08895c42018-02-01 09:37:32 -050032#Line # controls how Path Points are interpreted ##
Cary Clark73fa9722017-08-29 17:36:51 -040033
34#Code
35 enum Verb {
Cary Clark682c58d2018-05-16 07:07:07 -040036 kMove_Verb,
37 kLine_Verb,
38 kQuad_Verb,
39 kConic_Verb,
40 kCubic_Verb,
41 kClose_Verb,
42 kDone_Verb,
Cary Clark73fa9722017-08-29 17:36:51 -040043 };
44##
45
46Verb instructs Path how to interpret one or more Point and optional Conic_Weight;
Cary Clark8032b982017-07-28 11:04:54 -040047manage Contour, and terminate Path.
48
Cary Clark73fa9722017-08-29 17:36:51 -040049#Const kMove_Verb 0
Cary Clark682c58d2018-05-16 07:07:07 -040050#Line # starts new Contour at next Point ##
51 Consecutive kMove_Verb are preserved but all but the last kMove_Verb is
52 ignored. kMove_Verb after other Verbs implicitly closes the previous Contour
53 if SkPaint::kFill_Style is set when drawn; otherwise, stroke is drawn open.
54 kMove_Verb as the last Verb is preserved but ignored.
Cary Clark73fa9722017-08-29 17:36:51 -040055##
56#Const kLine_Verb 1
Cary Clark682c58d2018-05-16 07:07:07 -040057#Line # adds Line from Last_Point to next Point ##
58 Line is a straight segment from Point to Point. Consecutive kLine_Verb
59 extend Contour. kLine_Verb at same position as prior kMove_Verb is
60 preserved, and draws Point if SkPaint::kStroke_Style is set, and
61 SkPaint::Cap is SkPaint::kSquare_Cap or SkPaint::kRound_Cap. kLine_Verb
62 at same position as prior line or curve Verb is preserved but is ignored.
Cary Clark73fa9722017-08-29 17:36:51 -040063##
64#Const kQuad_Verb 2
Cary Clark682c58d2018-05-16 07:07:07 -040065#Line # adds Quad from Last_Point ##
66 Adds Quad from Last_Point, using control Point, and end Point.
Cary Clark73fa9722017-08-29 17:36:51 -040067 Quad is a parabolic section within tangents from Last_Point to control Point,
68 and control Point to end Point.
69##
70#Const kConic_Verb 3
Cary Clark682c58d2018-05-16 07:07:07 -040071#Line # adds Conic from Last_Point ##
Cary Clark73fa9722017-08-29 17:36:51 -040072 Adds Conic from Last_Point, using control Point, end Point, and Conic_Weight.
Cary Clark682c58d2018-05-16 07:07:07 -040073 Conic is a elliptical, parabolic, or hyperbolic section within tangents
Cary Clark73fa9722017-08-29 17:36:51 -040074 from Last_Point to control Point, and control Point to end Point, constrained
75 by Conic_Weight. Conic_Weight less than one is elliptical; equal to one is
76 parabolic (and identical to Quad); greater than one hyperbolic.
77##
78#Const kCubic_Verb 4
Cary Clark682c58d2018-05-16 07:07:07 -040079#Line # adds Cubic from Last_Point ##
80 Adds Cubic from Last_Point, using two control Points, and end Point.
Cary Clarka560c472017-11-27 10:44:06 -050081 Cubic is a third-order Bezier_Curve section within tangents from Last_Point
82 to first control Point, and from second control Point to end Point.
Cary Clark73fa9722017-08-29 17:36:51 -040083##
84#Const kClose_Verb 5
Cary Clark682c58d2018-05-16 07:07:07 -040085#Line # closes Contour ##
86 Closes Contour, connecting Last_Point to kMove_Verb Point. Consecutive
87 kClose_Verb are preserved but only first has an effect. kClose_Verb after
88 kMove_Verb has no effect.
Cary Clark73fa9722017-08-29 17:36:51 -040089##
90#Const kDone_Verb 6
Cary Clark682c58d2018-05-16 07:07:07 -040091#Line # terminates Path ##
92 Not in Verb_Array, but returned by Path iterator.
Cary Clark8032b982017-07-28 11:04:54 -040093##
94
95Each Verb has zero or more Points stored in Path.
96Path iterator returns complete curve descriptions, duplicating shared Points
97for consecutive entries.
98
99#Table
100#Legend
101# Verb # Allocated Points # Iterated Points # Weights ##
102##
103# kMove_Verb # 1 # 1 # 0 ##
104# kLine_Verb # 1 # 2 # 0 ##
105# kQuad_Verb # 2 # 3 # 0 ##
106# kConic_Verb # 2 # 3 # 1 ##
107# kCubic_Verb # 3 # 4 # 0 ##
108# kClose_Verb # 0 # 1 # 0 ##
109# kDone_Verb # -- # 0 # 0 ##
110##
Cary Clark73fa9722017-08-29 17:36:51 -0400111
112#Example
113void draw(SkCanvas* canvas) {
114 SkPath path;
115 path.lineTo(20, 20);
116 path.quadTo(-10, -10, 30, 30);
117 path.close();
118 path.cubicTo(1, 2, 3, 4, 5, 6);
119 path.conicTo(0, 0, 0, 0, 2);
120 uint8_t verbs[7];
121 int count = path.getVerbs(verbs, (int) SK_ARRAY_COUNT(verbs));
122 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close" };
123 SkDebugf("verb count: %d\nverbs: ", count);
124 for (int i = 0; i < count; ++i) {
125 SkDebugf("k%s_Verb ", verbStr[verbs[i]]);
126 }
127 SkDebugf("\n");
128}
129#StdOut
130verb count: 7
Cary Clark682c58d2018-05-16 07:07:07 -0400131verbs: kMove_Verb kLine_Verb kQuad_Verb kClose_Verb kMove_Verb kCubic_Verb kConic_Verb
Cary Clark73fa9722017-08-29 17:36:51 -0400132##
133##
134
135#Enum Verb ##
136#Subtopic Verb ##
137
138# ------------------------------------------------------------------------------
139#Subtopic Direction
Cary Clark682c58d2018-05-16 07:07:07 -0400140#Line # contour orientation, clockwise or counterclockwise ##
Cary Clark137b8742018-05-30 09:21:49 -0400141#Alias Directions ##
Cary Clark73fa9722017-08-29 17:36:51 -0400142
143#Enum Direction
Cary Clark08895c42018-02-01 09:37:32 -0500144#Line # sets Contour clockwise or counterclockwise ##
Cary Clark73fa9722017-08-29 17:36:51 -0400145
146#Code
Mike Klein36743362018-11-06 08:23:30 -0500147 enum Direction : int {
Cary Clark682c58d2018-05-16 07:07:07 -0400148 kCW_Direction,
149 kCCW_Direction,
Cary Clark73fa9722017-08-29 17:36:51 -0400150 };
151##
152
153Direction describes whether Contour is clockwise or counterclockwise.
154When Path contains multiple overlapping Contours, Direction together with
155Fill_Type determines whether overlaps are filled or form holes.
156
157Direction also determines how Contour is measured. For instance, dashing
158measures along Path to determine where to start and stop stroke; Direction
159will change dashed results as it steps clockwise or counterclockwise.
160
Cary Clark682c58d2018-05-16 07:07:07 -0400161Closed Contours like Rect, Round_Rect, Circle, and Oval added with
Cary Clark73fa9722017-08-29 17:36:51 -0400162kCW_Direction travel clockwise; the same added with kCCW_Direction
163travel counterclockwise.
164
165#Const kCW_Direction 0
Cary Clark682c58d2018-05-16 07:07:07 -0400166#Line # contour travels clockwise ##
Cary Clark73fa9722017-08-29 17:36:51 -0400167##
168#Const kCCW_Direction 1
Cary Clark682c58d2018-05-16 07:07:07 -0400169#Line # contour travels counterclockwise ##
Cary Clark73fa9722017-08-29 17:36:51 -0400170##
171
172
173#Example
174#Height 100
175void draw(SkCanvas* canvas) {
176 const SkPoint arrow[] = { {40, -5}, {45, 0}, {40, 5} };
177 const SkRect rect = {10, 10, 90, 90};
178 SkPaint rectPaint;
179 rectPaint.setAntiAlias(true);
180 SkPaint textPaint(rectPaint);
Cary Clark73fa9722017-08-29 17:36:51 -0400181 rectPaint.setStyle(SkPaint::kStroke_Style);
182 SkPaint arrowPaint(rectPaint);
183 SkPath arrowPath;
184 arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
185 arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 320, 0,
186 SkPath1DPathEffect::kRotate_Style));
187 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
188 canvas->drawRect(rect, rectPaint);
189 for (unsigned start : { 0, 1, 2, 3 } ) {
190 SkPath path;
191 path.addRect(rect, direction, start);
192 canvas->drawPath(path, arrowPaint);
193 }
194 canvas->drawString(SkPath::kCW_Direction == direction ? "CW" : "CCW", rect.centerX(),
195 rect.centerY(), textPaint);
196 canvas->translate(120, 0);
197 }
198}
199##
200
Cary Clark682c58d2018-05-16 07:07:07 -0400201#SeeAlso arcTo rArcTo isRect isNestedFillRects addRect addOval
Cary Clark73fa9722017-08-29 17:36:51 -0400202
203#Enum Direction ##
204#Subtopic Direction ##
205
206# ------------------------------------------------------------------------------
207
208#Method SkPath()
Cary Clark61313f32018-10-08 14:57:48 -0400209#In Constructors
Cary Clarkab2621d2018-01-30 10:08:57 -0500210#Line # constructs with default values ##
Cary Clark09d80c02018-10-31 12:14:03 -0400211#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400212
213#Example
214 SkPath path;
215 SkDebugf("path is " "%s" "empty", path.isEmpty() ? "" : "not ");
216#StdOut
217path is empty
218##
219##
220
221#SeeAlso reset rewind
222
223##
224
225# ------------------------------------------------------------------------------
226
227#Method SkPath(const SkPath& path)
Cary Clark61313f32018-10-08 14:57:48 -0400228#In Constructors
Cary Clarkab2621d2018-01-30 10:08:57 -0500229#Line # makes a shallow copy ##
Cary Clark09d80c02018-10-31 12:14:03 -0400230#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400231
232#Example
233#Description
234 Modifying one path does not effect another, even if they started as copies
235 of each other.
236##
237 SkPath path;
238 path.lineTo(20, 20);
239 SkPath path2(path);
240 path2.close();
241 SkDebugf("path verbs: %d\n", path.countVerbs());
242 SkDebugf("path2 verbs: %d\n", path2.countVerbs());
243 path.reset();
244 SkDebugf("after reset\n" "path verbs: %d\n", path.countVerbs());
245 SkDebugf("path2 verbs: %d\n", path2.countVerbs());
246#StdOut
247path verbs: 2
248path2 verbs: 3
249after reset
250path verbs: 0
251path2 verbs: 3
252##
253##
254
255#SeeAlso operator=(const SkPath& path)
256
257##
258
259# ------------------------------------------------------------------------------
260
261#Method ~SkPath()
262
Cary Clarkab2621d2018-01-30 10:08:57 -0500263#Line # decreases Reference_Count of owned objects ##
Cary Clark09d80c02018-10-31 12:14:03 -0400264#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400265
266#Example
267#Description
Cary Clark09d80c02018-10-31 12:14:03 -0400268delete calls Path destructor, but copy of original in path2 is unaffected.
Cary Clark73fa9722017-08-29 17:36:51 -0400269##
270void draw(SkCanvas* canvas) {
271 SkPath* path = new SkPath();
272 path->lineTo(20, 20);
273 SkPath path2(*path);
274 delete path;
275 SkDebugf("path2 is " "%s" "empty", path2.isEmpty() ? "" : "not ");
276}
277##
278
279#SeeAlso SkPath() SkPath(const SkPath& path) operator=(const SkPath& path)
280
281##
282
283# ------------------------------------------------------------------------------
284
285#Method SkPath& operator=(const SkPath& path)
286
Cary Clarkab2621d2018-01-30 10:08:57 -0500287#Line # makes a shallow copy ##
Cary Clark09d80c02018-10-31 12:14:03 -0400288#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400289
290#Example
291SkPath path1;
292path1.addRect({10, 20, 30, 40});
293SkPath path2 = path1;
294const SkRect& b1 = path1.getBounds();
295SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom);
296const SkRect& b2 = path2.getBounds();
297SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom);
298#StdOut
299path1 bounds = 10, 20, 30, 40
300path2 bounds = 10, 20, 30, 40
301#StdOut ##
302##
303
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400304#SeeAlso swap SkPath(const SkPath& path)
Cary Clark73fa9722017-08-29 17:36:51 -0400305
306##
307
308# ------------------------------------------------------------------------------
309
310#Method bool operator==(const SkPath& a, const SkPath& b)
311
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400312#Line # compares Paths for equality ##
Cary Clark09d80c02018-10-31 12:14:03 -0400313#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400314
315#Example
316#Description
Cary Clark77b3f3a2018-11-07 14:59:03 -0500317rewind() removes Verb_Array but leaves storage; since storage is not compared,
Cary Clark73fa9722017-08-29 17:36:51 -0400318Path pair are equivalent.
319##
320void draw(SkCanvas* canvas) {
321 auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void {
322 SkDebugf("%s one %c= two\n", prefix, a == b ? '=' : '!');
323 };
324 SkPath one;
325 SkPath two;
326 debugster("empty", one, two);
327 one.moveTo(0, 0);
328 debugster("moveTo", one, two);
329 one.rewind();
330 debugster("rewind", one, two);
331 one.moveTo(0, 0);
332 one.reset();
333 debugster("reset", one, two);
334}
335#StdOut
336empty one == two
337moveTo one != two
338rewind one == two
339reset one == two
340##
341##
342
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400343#SeeAlso operator!=(const SkPath& a, const SkPath& b) operator=(const SkPath& path)
344
Cary Clark73fa9722017-08-29 17:36:51 -0400345##
346
347# ------------------------------------------------------------------------------
348
Cary Clark682c58d2018-05-16 07:07:07 -0400349#Method bool operator!=(const SkPath& a, const SkPath& b)
Cary Clark73fa9722017-08-29 17:36:51 -0400350
Cary Clarkab2621d2018-01-30 10:08:57 -0500351#Line # compares paths for inequality ##
Cary Clark09d80c02018-10-31 12:14:03 -0400352#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400353
354#Example
355#Description
356Path pair are equal though their convexity is not equal.
357##
358void draw(SkCanvas* canvas) {
359 auto debugster = [](const char* prefix, const SkPath& a, const SkPath& b) -> void {
360 SkDebugf("%s one %c= two\n", prefix, a != b ? '!' : '=');
361 };
362 SkPath one;
363 SkPath two;
364 debugster("empty", one, two);
365 one.addRect({10, 20, 30, 40});
366 two.addRect({10, 20, 30, 40});
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400367 debugster("add rect", one, two);
Cary Clark73fa9722017-08-29 17:36:51 -0400368 one.setConvexity(SkPath::kConcave_Convexity);
369 debugster("setConvexity", one, two);
370 SkDebugf("convexity %c=\n", one.getConvexity() == two.getConvexity() ? '=' : '!');
371}
372#StdOut
373empty one == two
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400374add rect one == two
Cary Clark73fa9722017-08-29 17:36:51 -0400375setConvexity one == two
376convexity !=
377##
378##
379
380##
381
382# ------------------------------------------------------------------------------
383
Cary Clark4855f782018-02-06 09:41:53 -0500384#Subtopic Property
Cary Clark4855f782018-02-06 09:41:53 -0500385#Line # metrics and attributes ##
386##
Cary Clark73fa9722017-08-29 17:36:51 -0400387
Cary Clark4855f782018-02-06 09:41:53 -0500388#Method bool isInterpolatable(const SkPath& compare) const
389#In Property
390#In Interpolate
Cary Clarkab2621d2018-01-30 10:08:57 -0500391#Line # returns if pair contains equal counts of Verb_Array and Weights ##
Cary Clark09d80c02018-10-31 12:14:03 -0400392#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400393
394#Example
395 SkPath path, path2;
396 path.moveTo(20, 20);
397 path.lineTo(40, 40);
398 path.lineTo(20, 20);
399 path.lineTo(40, 40);
400 path.close();
401 path2.addRect({20, 20, 40, 40});
402 SkDebugf("paths are " "%s" "interpolatable", path.isInterpolatable(path2) ? "" : "not ");
403#StdOut
404paths are interpolatable
405##
406##
407
408#SeeAlso isInterpolatable
409
410##
411
412# ------------------------------------------------------------------------------
413
Cary Clark4855f782018-02-06 09:41:53 -0500414#Subtopic Interpolate
Cary Clark4855f782018-02-06 09:41:53 -0500415#Line # weighted average of Path pair ##
416##
Cary Clark73fa9722017-08-29 17:36:51 -0400417
Cary Clark4855f782018-02-06 09:41:53 -0500418#Method bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const
419#In Interpolate
Cary Clarkab2621d2018-01-30 10:08:57 -0500420#Line # interpolates between Path pair ##
Cary Clark80247e52018-07-11 16:18:41 -0400421Interpolates between Paths with Point_Array of equal size.
Cary Clark61dfc3a2018-01-03 08:37:53 -0500422Copy Verb_Array and Weights to out, and set out Point_Array to a weighted
423average of this Point_Array and ending Point_Array, using the formula:
Cary Clark2be81cf2018-09-13 12:04:30 -0400424#Formula # (Path Point * weight) + ending Point * (1 - weight) ##.
Cary Clark73fa9722017-08-29 17:36:51 -0400425
Cary Clark682c58d2018-05-16 07:07:07 -0400426weight is most useful when between zero (ending Point_Array) and
427one (this Point_Array); will work with values outside of this
Cary Clark73fa9722017-08-29 17:36:51 -0400428range.
429
430interpolate() returns false and leaves out unchanged if Point_Array is not
Cary Clark682c58d2018-05-16 07:07:07 -0400431the same size as ending Point_Array. Call isInterpolatable to check Path
Cary Clark73fa9722017-08-29 17:36:51 -0400432compatibility prior to calling interpolate().
433
434#Param ending Point_Array averaged with this Point_Array ##
Cary Clark682c58d2018-05-16 07:07:07 -0400435#Param weight contribution of this Point_Array, and
436 one minus contribution of ending Point_Array
Cary Clark73fa9722017-08-29 17:36:51 -0400437##
438#Param out Path replaced by interpolated averages ##
439
440#Return true if Paths contain same number of Points ##
441
442#Example
443#Height 60
444void draw(SkCanvas* canvas) {
445 SkPaint paint;
446 paint.setAntiAlias(true);
447 paint.setStyle(SkPaint::kStroke_Style);
448 SkPath path, path2;
449 path.moveTo(20, 20);
450 path.lineTo(40, 40);
451 path.lineTo(20, 40);
452 path.lineTo(40, 20);
453 path.close();
454 path2.addRect({20, 20, 40, 40});
455 for (SkScalar i = 0; i <= 1; i += 1.f / 6) {
456 SkPath interp;
457 path.interpolate(path2, i, &interp);
458 canvas->drawPath(interp, paint);
459 canvas->translate(30, 0);
460 }
461}
462##
463
464#SeeAlso isInterpolatable
465
466##
467
468# ------------------------------------------------------------------------------
469
Cary Clark682c58d2018-05-16 07:07:07 -0400470#Method bool unique() const
Cary Clark4855f782018-02-06 09:41:53 -0500471#Deprecated soon
472Only valid for Android framework.
Cary Clark73fa9722017-08-29 17:36:51 -0400473##
474
475# ------------------------------------------------------------------------------
Cary Clark8032b982017-07-28 11:04:54 -0400476#Subtopic Fill_Type
Cary Clark682c58d2018-05-16 07:07:07 -0400477#Line # fill rule, normal and inverted ##
Cary Clark8032b982017-07-28 11:04:54 -0400478
Cary Clark73fa9722017-08-29 17:36:51 -0400479#Enum FillType
Cary Clark08895c42018-02-01 09:37:32 -0500480#Line # sets winding rule and inverse fill ##
Cary Clark73fa9722017-08-29 17:36:51 -0400481
482#Code
483 enum FillType {
Cary Clark682c58d2018-05-16 07:07:07 -0400484 kWinding_FillType,
485 kEvenOdd_FillType,
486 kInverseWinding_FillType,
487 kInverseEvenOdd_FillType,
Cary Clark73fa9722017-08-29 17:36:51 -0400488 };
489##
Cary Clark8032b982017-07-28 11:04:54 -0400490
Cary Clark682c58d2018-05-16 07:07:07 -0400491Fill_Type selects the rule used to fill Path. Path set to kWinding_FillType
Cary Clark8032b982017-07-28 11:04:54 -0400492fills if the sum of Contour edges is not zero, where clockwise edges add one, and
493counterclockwise edges subtract one. Path set to kEvenOdd_FillType fills if the
Cary Clark682c58d2018-05-16 07:07:07 -0400494number of Contour edges is odd. Each Fill_Type has an inverse variant that
Cary Clark8032b982017-07-28 11:04:54 -0400495reverses the rule:
496kInverseWinding_FillType fills where the sum of Contour edges is zero;
497kInverseEvenOdd_FillType fills where the number of Contour edges is even.
498
499#Example
500#Height 100
501#Description
502The top row has two clockwise rectangles. The second row has one clockwise and
503one counterclockwise rectangle. The even-odd variants draw the same. The
504winding variants draw the top rectangle overlap, which has a winding of 2, the
505same as the outer parts of the top rectangles, which have a winding of 1.
506##
Cary Clark73fa9722017-08-29 17:36:51 -0400507void draw(SkCanvas* canvas) {
508 SkPath path;
509 path.addRect({10, 10, 30, 30}, SkPath::kCW_Direction);
510 path.addRect({20, 20, 40, 40}, SkPath::kCW_Direction);
511 path.addRect({10, 60, 30, 80}, SkPath::kCW_Direction);
512 path.addRect({20, 70, 40, 90}, SkPath::kCCW_Direction);
513 SkPaint strokePaint;
514 strokePaint.setStyle(SkPaint::kStroke_Style);
515 SkRect clipRect = {0, 0, 51, 100};
516 canvas->drawPath(path, strokePaint);
517 SkPaint fillPaint;
Cary Clark682c58d2018-05-16 07:07:07 -0400518 for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,
Cary Clark73fa9722017-08-29 17:36:51 -0400519 SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) {
520 canvas->translate(51, 0);
521 canvas->save();
522 canvas->clipRect(clipRect);
523 path.setFillType(fillType);
524 canvas->drawPath(path, fillPaint);
525 canvas->restore();
526 }
Cary Clark8032b982017-07-28 11:04:54 -0400527}
528##
Cary Clark73fa9722017-08-29 17:36:51 -0400529
530#Const kWinding_FillType 0
Cary Clark682c58d2018-05-16 07:07:07 -0400531#Line # is enclosed by a non-zero sum of Contour Directions ##
Cary Clark73fa9722017-08-29 17:36:51 -0400532##
533#Const kEvenOdd_FillType 1
Cary Clark682c58d2018-05-16 07:07:07 -0400534#Line # is enclosed by an odd number of Contours ##
Cary Clark73fa9722017-08-29 17:36:51 -0400535##
536#Const kInverseWinding_FillType 2
Cary Clark682c58d2018-05-16 07:07:07 -0400537#Line # is enclosed by a zero sum of Contour Directions ##
Cary Clark73fa9722017-08-29 17:36:51 -0400538##
539#Const kInverseEvenOdd_FillType 3
Cary Clark682c58d2018-05-16 07:07:07 -0400540#Line # is enclosed by an even number of Contours ##
Cary Clark73fa9722017-08-29 17:36:51 -0400541##
542
543#Example
544#Height 230
545void draw(SkCanvas* canvas) {
546 SkPath path;
547 path.addRect({20, 10, 80, 70}, SkPath::kCW_Direction);
548 path.addRect({40, 30, 100, 90}, SkPath::kCW_Direction);
549 SkPaint strokePaint;
550 strokePaint.setStyle(SkPaint::kStroke_Style);
551 SkRect clipRect = {0, 0, 128, 128};
552 canvas->drawPath(path, strokePaint);
553 canvas->drawLine({0, 50}, {120, 50}, strokePaint);
554 SkPaint textPaint;
555 textPaint.setAntiAlias(true);
Cary Clark73fa9722017-08-29 17:36:51 -0400556 SkScalar textHPos[] = { 10, 30, 60, 90, 110 };
557 canvas->drawPosTextH("01210", 5, textHPos, 48, textPaint);
558 textPaint.setTextSize(18);
559 canvas->translate(0, 128);
560 canvas->scale(.5f, .5f);
561 canvas->drawString("inverse", 384, 150, textPaint);
562 SkPaint fillPaint;
Cary Clark682c58d2018-05-16 07:07:07 -0400563 for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,
Cary Clark73fa9722017-08-29 17:36:51 -0400564 SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) {
565 canvas->save();
566 canvas->clipRect(clipRect);
567 path.setFillType(fillType);
568 canvas->drawPath(path, fillPaint);
569 canvas->restore();
570 canvas->drawString(fillType & 1 ? "even-odd" : "winding", 64, 170, textPaint);
571 canvas->translate(128, 0);
572 }
573}
574##
575
576#SeeAlso SkPaint::Style Direction getFillType setFillType
577
578##
579
580# ------------------------------------------------------------------------------
581
Cary Clark682c58d2018-05-16 07:07:07 -0400582#Method FillType getFillType() const
Cary Clark73fa9722017-08-29 17:36:51 -0400583
Cary Clarkab2621d2018-01-30 10:08:57 -0500584#In Fill_Type
585#Line # returns Fill_Type: winding, even-odd, inverse ##
Cary Clark09d80c02018-10-31 12:14:03 -0400586#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400587
588#Example
589 SkPath path;
590 SkDebugf("default path fill type is %s\n",
591 path.getFillType() == SkPath::kWinding_FillType ? "kWinding_FillType" :
Cary Clark682c58d2018-05-16 07:07:07 -0400592 path.getFillType() == SkPath::kEvenOdd_FillType ? "kEvenOdd_FillType" :
Cary Clark73fa9722017-08-29 17:36:51 -0400593 path.getFillType() == SkPath::kInverseWinding_FillType ? "kInverseWinding_FillType" :
594 "kInverseEvenOdd_FillType");
595#StdOut
596default path fill type is kWinding_FillType
597##
598##
599
600#SeeAlso FillType setFillType isInverseFillType
601
602##
603
604# ------------------------------------------------------------------------------
605
Cary Clark682c58d2018-05-16 07:07:07 -0400606#Method void setFillType(FillType ft)
Cary Clark73fa9722017-08-29 17:36:51 -0400607
Cary Clarkab2621d2018-01-30 10:08:57 -0500608#In Fill_Type
609#Line # sets Fill_Type: winding, even-odd, inverse ##
Cary Clark09d80c02018-10-31 12:14:03 -0400610#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400611
612#Example
613#Description
614If empty Path is set to inverse FillType, it fills all pixels.
615##
616#Height 64
617 SkPath path;
618 path.setFillType(SkPath::kInverseWinding_FillType);
619 SkPaint paint;
620 paint.setColor(SK_ColorBLUE);
621 canvas->drawPath(path, paint);
622##
623
624#SeeAlso FillType getFillType toggleInverseFillType
625
626##
627
628# ------------------------------------------------------------------------------
629
Cary Clark682c58d2018-05-16 07:07:07 -0400630#Method bool isInverseFillType() const
Cary Clark73fa9722017-08-29 17:36:51 -0400631
Cary Clarkab2621d2018-01-30 10:08:57 -0500632#In Fill_Type
633#Line # returns if Fill_Type fills outside geometry ##
Cary Clark09d80c02018-10-31 12:14:03 -0400634#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400635
636#Example
637 SkPath path;
638 SkDebugf("default path fill type is inverse: %s\n",
639 path.isInverseFillType() ? "true" : "false");
640#StdOut
641default path fill type is inverse: false
642##
643##
644
645#SeeAlso FillType getFillType setFillType toggleInverseFillType
646
647##
648
649# ------------------------------------------------------------------------------
650
Cary Clark682c58d2018-05-16 07:07:07 -0400651#Method void toggleInverseFillType()
Cary Clark73fa9722017-08-29 17:36:51 -0400652
Cary Clarkab2621d2018-01-30 10:08:57 -0500653#In Fill_Type
654#Line # toggles Fill_Type between inside and outside geometry ##
Cary Clark80247e52018-07-11 16:18:41 -0400655Replaces FillType with its inverse. The inverse of FillType describes the area
Cary Clark73fa9722017-08-29 17:36:51 -0400656unmodified by the original FillType.
657
Cary Clark682c58d2018-05-16 07:07:07 -0400658#Table
Cary Clark73fa9722017-08-29 17:36:51 -0400659#Legend
Cary Clark682c58d2018-05-16 07:07:07 -0400660# FillType # toggled FillType ##
Cary Clark73fa9722017-08-29 17:36:51 -0400661##
662# kWinding_FillType # kInverseWinding_FillType ##
663# kEvenOdd_FillType # kInverseEvenOdd_FillType ##
664# kInverseWinding_FillType # kWinding_FillType ##
665# kInverseEvenOdd_FillType # kEvenOdd_FillType ##
666##
667
668#Example
669#Description
670Path drawn normally and through its inverse touches every pixel once.
671##
672#Height 100
673SkPath path;
674SkPaint paint;
675paint.setColor(SK_ColorRED);
Cary Clark8032b982017-07-28 11:04:54 -0400676paint.setTextSize(80);
677paint.getTextPath("ABC", 3, 20, 80, &path);
Cary Clark73fa9722017-08-29 17:36:51 -0400678canvas->drawPath(path, paint);
679path.toggleInverseFillType();
680paint.setColor(SK_ColorGREEN);
681canvas->drawPath(path, paint);
682##
683
684#SeeAlso FillType getFillType setFillType isInverseFillType
685
686##
687
Cary Clark8032b982017-07-28 11:04:54 -0400688#Subtopic Fill_Type ##
Cary Clark73fa9722017-08-29 17:36:51 -0400689
690# ------------------------------------------------------------------------------
Cary Clark8032b982017-07-28 11:04:54 -0400691
692#Subtopic Convexity
Cary Clark08895c42018-02-01 09:37:32 -0500693#Line # if Path is concave or convex ##
Cary Clark73fa9722017-08-29 17:36:51 -0400694
695#Enum Convexity
Cary Clark08895c42018-02-01 09:37:32 -0500696#Line # returns if Path is convex or concave ##
Cary Clark73fa9722017-08-29 17:36:51 -0400697
698#Code
Cary Clark884dd7d2017-10-11 10:37:52 -0400699 enum Convexity : uint8_t {
Cary Clark682c58d2018-05-16 07:07:07 -0400700 kUnknown_Convexity,
Cary Clark73fa9722017-08-29 17:36:51 -0400701 kConvex_Convexity,
Cary Clark682c58d2018-05-16 07:07:07 -0400702 kConcave_Convexity,
Cary Clark73fa9722017-08-29 17:36:51 -0400703 };
704##
705
Cary Clark682c58d2018-05-16 07:07:07 -0400706Path is convex if it contains one Contour and Contour loops no more than
707360 degrees, and Contour angles all have same Direction. Convex Path
Cary Clark8032b982017-07-28 11:04:54 -0400708may have better performance and require fewer resources on GPU_Surface.
709
Cary Clark73fa9722017-08-29 17:36:51 -0400710Path is concave when either at least one Direction change is clockwise and
711another is counterclockwise, or the sum of the changes in Direction is not 360
712degrees.
713
Cary Clark682c58d2018-05-16 07:07:07 -0400714Initially Path Convexity is kUnknown_Convexity. Path Convexity is computed
Cary Clark73fa9722017-08-29 17:36:51 -0400715if needed by destination Surface.
716
717#Const kUnknown_Convexity 0
Cary Clark682c58d2018-05-16 07:07:07 -0400718#Line # indicates Convexity has not been determined ##
Cary Clark73fa9722017-08-29 17:36:51 -0400719##
720#Const kConvex_Convexity 1
Cary Clark682c58d2018-05-16 07:07:07 -0400721#Line # one Contour made of a simple geometry without indentations ##
Cary Clark73fa9722017-08-29 17:36:51 -0400722##
723#Const kConcave_Convexity 2
Cary Clark682c58d2018-05-16 07:07:07 -0400724#Line # more than one Contour, or a geometry with indentations ##
Cary Clark73fa9722017-08-29 17:36:51 -0400725##
726
727#Example
728void draw(SkCanvas* canvas) {
729 SkPaint paint;
Cary Clark682c58d2018-05-16 07:07:07 -0400730 SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
Cary Clark73fa9722017-08-29 17:36:51 -0400731 const char* labels[] = { "unknown", "convex", "concave" };
732 for (SkScalar x : { 40, 100 } ) {
733 SkPath path;
734 quad[0].fX = x;
735 path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
736 canvas->drawPath(path, paint);
737 canvas->drawString(labels[(int) path.getConvexity()], 30, 100, paint);
738 canvas->translate(100, 100);
739 }
740}
741##
742
743#SeeAlso Contour Direction getConvexity getConvexityOrUnknown setConvexity isConvex
744
745#Enum Convexity ##
746
Cary Clark682c58d2018-05-16 07:07:07 -0400747#Method Convexity getConvexity() const
Cary Clark73fa9722017-08-29 17:36:51 -0400748
Cary Clarkab2621d2018-01-30 10:08:57 -0500749#In Convexity
750#Line # returns geometry convexity, computing if necessary ##
Cary Clark09d80c02018-10-31 12:14:03 -0400751#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400752
753#Example
754void draw(SkCanvas* canvas) {
755 auto debugster = [](const char* prefix, const SkPath& path) -> void {
Cary Clark682c58d2018-05-16 07:07:07 -0400756 SkDebugf("%s path convexity is %s\n", prefix,
Cary Clark73fa9722017-08-29 17:36:51 -0400757 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" :
758 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); };
759 SkPath path;
760 debugster("initial", path);
761 path.lineTo(50, 0);
762 debugster("first line", path);
763 path.lineTo(50, 50);
764 debugster("second line", path);
765 path.lineTo(100, 50);
766 debugster("third line", path);
767}
768##
769
770#SeeAlso Convexity Contour Direction getConvexityOrUnknown setConvexity isConvex
771
772##
773
774# ------------------------------------------------------------------------------
775
Cary Clark682c58d2018-05-16 07:07:07 -0400776#Method Convexity getConvexityOrUnknown() const
Cary Clark73fa9722017-08-29 17:36:51 -0400777
Cary Clarkab2621d2018-01-30 10:08:57 -0500778#In Convexity
779#Line # returns geometry convexity if known ##
Cary Clark09d80c02018-10-31 12:14:03 -0400780#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400781
782#Example
783#Description
784Convexity is unknown unless getConvexity is called without a subsequent call
785that alters the path.
786##
787void draw(SkCanvas* canvas) {
788 auto debugster = [](const char* prefix, const SkPath& path) -> void {
Cary Clark682c58d2018-05-16 07:07:07 -0400789 SkDebugf("%s path convexity is %s\n", prefix,
Cary Clark73fa9722017-08-29 17:36:51 -0400790 SkPath::kUnknown_Convexity == path.getConvexityOrUnknown() ? "unknown" :
791 SkPath::kConvex_Convexity == path.getConvexityOrUnknown() ? "convex" : "concave"); };
792 SkPath path;
793 debugster("initial", path);
794 path.lineTo(50, 0);
795 debugster("first line", path);
796 path.getConvexity();
797 path.lineTo(50, 50);
798 debugster("second line", path);
799 path.lineTo(100, 50);
800 path.getConvexity();
801 debugster("third line", path);
802}
803##
804
805#SeeAlso Convexity Contour Direction getConvexity setConvexity isConvex
806
807##
808
809# ------------------------------------------------------------------------------
810
811#Method void setConvexity(Convexity convexity)
812
Cary Clarkab2621d2018-01-30 10:08:57 -0500813#In Convexity
814#Line # sets if geometry is convex to avoid future computation ##
Cary Clark09d80c02018-10-31 12:14:03 -0400815#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400816
817#Example
818void draw(SkCanvas* canvas) {
819 auto debugster = [](const char* prefix, const SkPath& path) -> void {
Cary Clark682c58d2018-05-16 07:07:07 -0400820 SkDebugf("%s path convexity is %s\n", prefix,
Cary Clark73fa9722017-08-29 17:36:51 -0400821 SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" :
822 SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); };
Cary Clark682c58d2018-05-16 07:07:07 -0400823 SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
Cary Clark73fa9722017-08-29 17:36:51 -0400824 SkPath path;
825 path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
826 debugster("initial", path);
827 path.setConvexity(SkPath::kConcave_Convexity);
828 debugster("after forcing concave", path);
829 path.setConvexity(SkPath::kUnknown_Convexity);
830 debugster("after forcing unknown", path);
831}
832##
833
834#SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown isConvex
835
836##
837
838# ------------------------------------------------------------------------------
839
Cary Clark682c58d2018-05-16 07:07:07 -0400840#Method bool isConvex() const
Cary Clark73fa9722017-08-29 17:36:51 -0400841
Cary Clarkab2621d2018-01-30 10:08:57 -0500842#In Convexity
843#Line # returns if geometry is convex ##
Cary Clark09d80c02018-10-31 12:14:03 -0400844#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400845
846#Example
847#Description
Cary Clark682c58d2018-05-16 07:07:07 -0400848Concave shape is erroneously considered convex after a forced call to
Cary Clark73fa9722017-08-29 17:36:51 -0400849setConvexity.
850##
851void draw(SkCanvas* canvas) {
852 SkPaint paint;
Cary Clark682c58d2018-05-16 07:07:07 -0400853 SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
Cary Clark73fa9722017-08-29 17:36:51 -0400854 for (SkScalar x : { 40, 100 } ) {
855 SkPath path;
856 quad[0].fX = x;
857 path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
858 path.setConvexity(SkPath::kConvex_Convexity);
859 canvas->drawPath(path, paint);
860 canvas->drawString(path.isConvex() ? "convex" : "not convex", 30, 100, paint);
861 canvas->translate(100, 100);
862 }
863}
864##
865
866#SeeAlso Convexity Contour Direction getConvexity getConvexityOrUnknown setConvexity
867
868##
869
Cary Clark73fa9722017-08-29 17:36:51 -0400870#Subtopic Convexity ##
871
872# ------------------------------------------------------------------------------
873
Mike Reed0c3137c2018-02-20 13:57:05 -0500874#Method bool isOval(SkRect* bounds) const
Cary Clark4855f782018-02-06 09:41:53 -0500875#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -0500876#Line # returns if describes Oval ##
Cary Clark09d80c02018-10-31 12:14:03 -0400877#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400878
879#Example
880void draw(SkCanvas* canvas) {
881 SkPaint paint;
882 SkPath path;
Mike Reed0c3137c2018-02-20 13:57:05 -0500883 path.addOval({20, 20, 220, 220});
Cary Clark73fa9722017-08-29 17:36:51 -0400884 SkRect bounds;
Mike Reed0c3137c2018-02-20 13:57:05 -0500885 if (path.isOval(&bounds)) {
886 paint.setColor(0xFF9FBFFF);
887 canvas->drawRect(bounds, paint);
888 }
Cary Clark73fa9722017-08-29 17:36:51 -0400889 paint.setColor(0x3f000000);
890 canvas->drawPath(path, paint);
Cary Clark73fa9722017-08-29 17:36:51 -0400891}
892##
893
894#SeeAlso Oval addCircle addOval
895
896##
897
898# ------------------------------------------------------------------------------
899
Mike Reed0c3137c2018-02-20 13:57:05 -0500900#Method bool isRRect(SkRRect* rrect) const
Cary Clark4855f782018-02-06 09:41:53 -0500901#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -0500902#Line # returns if describes Round_Rect ##
Cary Clark09d80c02018-10-31 12:14:03 -0400903#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400904
905#Example
Cary Clarkce101242017-09-01 15:51:02 -0400906#Description
Mike Reed0c3137c2018-02-20 13:57:05 -0500907Draw rounded rectangle and its bounds.
Cary Clarkce101242017-09-01 15:51:02 -0400908##
Cary Clark73fa9722017-08-29 17:36:51 -0400909void draw(SkCanvas* canvas) {
910 SkPaint paint;
911 SkPath path;
Mike Reed0c3137c2018-02-20 13:57:05 -0500912 path.addRRect(SkRRect::MakeRectXY({20, 20, 220, 220}, 30, 50));
Cary Clark73fa9722017-08-29 17:36:51 -0400913 SkRRect rrect;
Mike Reed0c3137c2018-02-20 13:57:05 -0500914 if (path.isRRect(&rrect)) {
915 const SkRect& bounds = rrect.rect();
916 paint.setColor(0xFF9FBFFF);
917 canvas->drawRect(bounds, paint);
918 }
Cary Clark73fa9722017-08-29 17:36:51 -0400919 paint.setColor(0x3f000000);
920 canvas->drawPath(path, paint);
Cary Clark73fa9722017-08-29 17:36:51 -0400921}
922##
923
Cary Clark682c58d2018-05-16 07:07:07 -0400924#SeeAlso Round_Rect addRoundRect addRRect
Cary Clark73fa9722017-08-29 17:36:51 -0400925
926##
927
928# ------------------------------------------------------------------------------
929
Cary Clark0251b1b2018-08-15 15:14:55 -0400930#Method SkPath& reset()
Cary Clark61313f32018-10-08 14:57:48 -0400931#In Constructors
Cary Clarkab2621d2018-01-30 10:08:57 -0500932#Line # removes Verb_Array, Point_Array, and Weights; frees memory ##
Cary Clark09d80c02018-10-31 12:14:03 -0400933#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -0400934
Cary Clark73fa9722017-08-29 17:36:51 -0400935#Example
936 SkPath path1, path2;
937 path1.setFillType(SkPath::kInverseWinding_FillType);
938 path1.addRect({10, 20, 30, 40});
939 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
940 path1.reset();
941 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
942##
943
944#SeeAlso rewind()
945
946##
947
948# ------------------------------------------------------------------------------
949
Cary Clark0251b1b2018-08-15 15:14:55 -0400950#Method SkPath& rewind()
Cary Clark61313f32018-10-08 14:57:48 -0400951#In Constructors
Cary Clarkab2621d2018-01-30 10:08:57 -0500952#Line # removes Verb_Array, Point_Array, and Weights, keeping memory ##
Cary Clark09d80c02018-10-31 12:14:03 -0400953#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -0400954
Cary Clark73fa9722017-08-29 17:36:51 -0400955#Example
956#Description
957Although path1 retains its internal storage, it is indistinguishable from
958a newly initialized path.
959##
960 SkPath path1, path2;
961 path1.setFillType(SkPath::kInverseWinding_FillType);
962 path1.addRect({10, 20, 30, 40});
963 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
964 path1.rewind();
965 SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
966##
967
968#SeeAlso reset()
969
970##
971
972# ------------------------------------------------------------------------------
973
Cary Clark682c58d2018-05-16 07:07:07 -0400974#Method bool isEmpty() const
Cary Clark4855f782018-02-06 09:41:53 -0500975#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -0500976#Line # returns if verb count is zero ##
Cary Clark09d80c02018-10-31 12:14:03 -0400977#Populate
Cary Clark73fa9722017-08-29 17:36:51 -0400978
979#Example
980void draw(SkCanvas* canvas) {
981 auto debugster = [](const char* prefix, const SkPath& path) -> void {
982 SkDebugf("%s path is %s" "empty\n", prefix, path.isEmpty() ? "" : "not ");
983 };
984 SkPath path;
985 debugster("initial", path);
986 path.moveTo(0, 0);
987 debugster("after moveTo", path);
988 path.rewind();
989 debugster("after rewind", path);
990 path.lineTo(0, 0);
991 debugster("after lineTo", path);
992 path.reset();
993 debugster("after reset", path);
994}
995#StdOut
996initial path is empty
997after moveTo path is not empty
998after rewind path is empty
999after lineTo path is not empty
1000after reset path is empty
1001##
1002##
1003
1004#SeeAlso SkPath() reset() rewind()
1005
1006##
1007
1008# ------------------------------------------------------------------------------
1009
1010#Method bool isLastContourClosed() const
Cary Clark4855f782018-02-06 09:41:53 -05001011#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001012#Line # returns if final Contour forms a loop ##
Cary Clark09d80c02018-10-31 12:14:03 -04001013#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001014
1015#Example
1016#Description
1017close() has no effect if Path is empty; isLastContourClosed() returns
1018false until Path has geometry followed by close().
1019##
1020void draw(SkCanvas* canvas) {
1021 auto debugster = [](const char* prefix, const SkPath& path) -> void {
1022 SkDebugf("%s last contour is %s" "closed\n", prefix,
1023 path.isLastContourClosed() ? "" : "not ");
1024 };
1025 SkPath path;
1026 debugster("initial", path);
1027 path.close();
1028 debugster("after close", path);
1029 path.lineTo(0, 0);
1030 debugster("after lineTo", path);
1031 path.close();
1032 debugster("after close", path);
1033}
1034#StdOut
1035initial last contour is not closed
1036after close last contour is not closed
1037after lineTo last contour is not closed
1038after close last contour is closed
1039##
1040##
1041
1042#SeeAlso close()
1043
1044##
1045
1046# ------------------------------------------------------------------------------
1047
Cary Clark682c58d2018-05-16 07:07:07 -04001048#Method bool isFinite() const
Cary Clark4855f782018-02-06 09:41:53 -05001049#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001050#Line # returns if all Point values are finite ##
Cary Clark09d80c02018-10-31 12:14:03 -04001051#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001052
1053#Example
1054void draw(SkCanvas* canvas) {
1055 auto debugster = [](const char* prefix, const SkPath& path) -> void {
1056 SkDebugf("%s path is %s" "finite\n", prefix, path.isFinite() ? "" : "not ");
1057 };
1058 SkPath path;
1059 debugster("initial", path);
1060 path.lineTo(SK_ScalarMax, SK_ScalarMax);
1061 debugster("after line", path);
1062 SkMatrix matrix;
1063 matrix.setScale(2, 2);
1064 path.transform(matrix);
1065 debugster("after scale", path);
1066}
1067#StdOut
1068initial path is finite
1069after line path is finite
1070after scale path is not finite
1071##
1072##
1073
1074#SeeAlso SkScalar
1075##
1076
1077# ------------------------------------------------------------------------------
1078
Cary Clark682c58d2018-05-16 07:07:07 -04001079#Method bool isVolatile() const
Cary Clark4855f782018-02-06 09:41:53 -05001080#In Property
1081#In Volatile
Cary Clarkab2621d2018-01-30 10:08:57 -05001082#Line # returns if Device should not cache ##
Cary Clark09d80c02018-10-31 12:14:03 -04001083#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001084
1085#Example
1086 SkPath path;
1087 SkDebugf("volatile by default is %s\n", path.isVolatile() ? "true" : "false");
1088#StdOut
1089volatile by default is false
1090##
1091##
1092
1093#SeeAlso setIsVolatile
1094
1095##
1096
1097# ------------------------------------------------------------------------------
Cary Clark4855f782018-02-06 09:41:53 -05001098#Subtopic Volatile
Cary Clark4855f782018-02-06 09:41:53 -05001099#Line # caching attribute ##
1100##
Cary Clark73fa9722017-08-29 17:36:51 -04001101
Cary Clark682c58d2018-05-16 07:07:07 -04001102#Method void setIsVolatile(bool isVolatile)
Cary Clark4855f782018-02-06 09:41:53 -05001103#In Volatile
Cary Clarkab2621d2018-01-30 10:08:57 -05001104#Line # sets if Device should not cache ##
Cary Clark09d80c02018-10-31 12:14:03 -04001105#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001106
1107#Example
1108#Height 50
1109#Width 50
1110 SkPaint paint;
1111 paint.setStyle(SkPaint::kStroke_Style);
1112 SkPath path;
1113 path.setIsVolatile(true);
1114 path.lineTo(40, 40);
1115 canvas->drawPath(path, paint);
1116 path.rewind();
1117 path.moveTo(0, 40);
1118 path.lineTo(40, 0);
1119 canvas->drawPath(path, paint);
1120##
1121
1122#ToDo tie example to bench to show how volatile affects speed or dm to show resource usage ##
1123
1124#SeeAlso isVolatile
1125
1126##
1127
1128# ------------------------------------------------------------------------------
1129
Cary Clark682c58d2018-05-16 07:07:07 -04001130#Method static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact)
Cary Clark4855f782018-02-06 09:41:53 -05001131#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001132#Line # returns if Line is very small ##
Cary Clark09d80c02018-10-31 12:14:03 -04001133#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001134
1135#Example
1136#Description
Cary Clarkce101242017-09-01 15:51:02 -04001137As single precision floats, 100 and 100.000001 have the same bit representation,
1138and are exactly equal. 100 and 100.0001 have different bit representations, and
Cary Clark73fa9722017-08-29 17:36:51 -04001139are not exactly equal, but are nearly equal.
1140##
1141void draw(SkCanvas* canvas) {
1142 SkPoint points[] = { {100, 100}, {100.000001f, 100.000001f}, {100.0001f, 100.0001f} };
1143 for (size_t i = 0; i < SK_ARRAY_COUNT(points) - 1; ++i) {
1144 for (bool exact : { false, true } ) {
1145 SkDebugf("line from (%1.8g,%1.8g) to (%1.8g,%1.8g) is %s" "degenerate, %s\n",
1146 points[i].fX, points[i].fY, points[i + 1].fX, points[i + 1].fY,
1147 SkPath::IsLineDegenerate(points[i], points[i + 1], exact)
1148 ? "" : "not ", exact ? "exactly" : "nearly");
1149 }
1150 }
1151}
1152#StdOut
1153line from (100,100) to (100,100) is degenerate, nearly
1154line from (100,100) to (100,100) is degenerate, exactly
1155line from (100,100) to (100.0001,100.0001) is degenerate, nearly
1156line from (100,100) to (100.0001,100.0001) is not degenerate, exactly
1157#StdOut ##
1158##
1159
Cary Clark682c58d2018-05-16 07:07:07 -04001160#SeeAlso IsQuadDegenerate IsCubicDegenerate
Cary Clark73fa9722017-08-29 17:36:51 -04001161##
1162
1163# ------------------------------------------------------------------------------
1164
1165#Method static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
Cary Clark682c58d2018-05-16 07:07:07 -04001166 const SkPoint& p3, bool exact)
Cary Clark4855f782018-02-06 09:41:53 -05001167#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001168#Line # returns if Quad is very small ##
Cary Clark09d80c02018-10-31 12:14:03 -04001169#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001170
1171#Example
1172#Description
Cary Clarkce101242017-09-01 15:51:02 -04001173As single precision floats: 100, 100.00001, and 100.00002 have different bit representations
Cary Clark73fa9722017-08-29 17:36:51 -04001174but nearly the same value. Translating all three by 1000 gives them the same bit representation;
Cary Clarkce101242017-09-01 15:51:02 -04001175the fractional portion of the number can not be represented by the float and is lost.
Cary Clark73fa9722017-08-29 17:36:51 -04001176##
1177void draw(SkCanvas* canvas) {
1178 auto debugster = [](const SkPath& path, bool exact) -> void {
Cary Clark682c58d2018-05-16 07:07:07 -04001179 SkDebugf("quad (%1.8g,%1.8g), (%1.8g,%1.8g), (%1.8g,%1.8g) is %s" "degenerate, %s\n",
Cary Clark73fa9722017-08-29 17:36:51 -04001180 path.getPoint(0).fX, path.getPoint(0).fY, path.getPoint(1).fX,
1181 path.getPoint(1).fY, path.getPoint(2).fX, path.getPoint(2).fY,
1182 SkPath::IsQuadDegenerate(path.getPoint(0), path.getPoint(1), path.getPoint(2), exact) ?
1183 "" : "not ", exact ? "exactly" : "nearly");
1184 };
1185 SkPath path, offset;
1186 path.moveTo({100, 100});
1187 path.quadTo({100.00001f, 100.00001f}, {100.00002f, 100.00002f});
1188 offset.addPath(path, 1000, 1000);
1189 for (bool exact : { false, true } ) {
1190 debugster(path, exact);
1191 debugster(offset, exact);
1192 }
1193}
1194#StdOut
1195quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is degenerate, nearly
1196quad (1100,1100), (1100,1100), (1100,1100) is degenerate, nearly
1197quad (100,100), (100.00001,100.00001), (100.00002,100.00002) is not degenerate, exactly
1198quad (1100,1100), (1100,1100), (1100,1100) is degenerate, exactly
1199#StdOut ##
1200##
1201
Cary Clark682c58d2018-05-16 07:07:07 -04001202#SeeAlso IsLineDegenerate IsCubicDegenerate
Cary Clark73fa9722017-08-29 17:36:51 -04001203##
1204
1205# ------------------------------------------------------------------------------
1206
1207#Method static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
Cary Clark682c58d2018-05-16 07:07:07 -04001208 const SkPoint& p3, const SkPoint& p4, bool exact)
Cary Clark4855f782018-02-06 09:41:53 -05001209#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001210#Line # returns if Cubic is very small ##
Cary Clark09d80c02018-10-31 12:14:03 -04001211#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001212
1213#Example
1214void draw(SkCanvas* canvas) {
1215 SkPoint points[] = {{1, 0}, {0, 0}, {0, 0}, {0, 0}};
1216 SkScalar step = 1;
Cary Clark75fd4492018-06-20 12:45:16 -04001217 SkScalar prior, length = 0, degenerate = 0;
Cary Clark73fa9722017-08-29 17:36:51 -04001218 do {
1219 prior = points[0].fX;
1220 step /= 2;
1221 if (SkPath::IsCubicDegenerate(points[0], points[1], points[2], points[3], false)) {
1222 degenerate = prior;
1223 points[0].fX += step;
1224 } else {
1225 length = prior;
1226 points[0].fX -= step;
1227 }
1228 } while (prior != points[0].fX);
1229 SkDebugf("%1.8g is degenerate\n", degenerate);
1230 SkDebugf("%1.8g is length\n", length);
1231}
1232#StdOut
12330.00024414062 is degenerate
12340.00024414065 is length
1235#StdOut ##
1236##
1237
1238##
1239
1240# ------------------------------------------------------------------------------
1241
1242#Method bool isLine(SkPoint line[2]) const
Cary Clark4855f782018-02-06 09:41:53 -05001243#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001244#Line # returns if describes Line ##
Cary Clark09d80c02018-10-31 12:14:03 -04001245#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001246
1247#Example
1248void draw(SkCanvas* canvas) {
1249 auto debugster = [](const char* prefix, const SkPath& path) -> void {
1250 SkPoint line[2];
1251 if (path.isLine(line)) {
1252 SkDebugf("%s is line (%1.8g,%1.8g) (%1.8g,%1.8g)\n", prefix,
1253 line[0].fX, line[0].fY, line[1].fX, line[1].fY);
1254 } else {
1255 SkDebugf("%s is not line\n", prefix);
1256 }
1257 };
1258 SkPath path;
1259 debugster("empty", path);
1260 path.lineTo(0, 0);
1261 debugster("zero line", path);
1262 path.rewind();
1263 path.moveTo(10, 10);
1264 path.lineTo(20, 20);
1265 debugster("line", path);
1266 path.moveTo(20, 20);
1267 debugster("second move", path);
1268}
1269#StdOut
1270empty is not line
1271zero line is line (0,0) (0,0)
1272line is line (10,10) (20,20)
1273second move is not line
1274##
1275##
1276
1277##
1278
1279# ------------------------------------------------------------------------------
1280
Cary Clark8032b982017-07-28 11:04:54 -04001281#Subtopic Point_Array
Cary Clark08895c42018-02-01 09:37:32 -05001282#Line # end points and control points for lines and curves ##
Cary Clark61dfc3a2018-01-03 08:37:53 -05001283#Substitute SkPoint array
Cary Clark8032b982017-07-28 11:04:54 -04001284
1285Point_Array contains Points satisfying the allocated Points for
1286each Verb in Verb_Array. For instance, Path containing one Contour with Line
Cary Clark77b3f3a2018-11-07 14:59:03 -05001287and Quad is described by Verb_Array: kMove_Verb, kLine_Verb, kQuad_Verb; and
Cary Clark8032b982017-07-28 11:04:54 -04001288one Point for move, one Point for Line, two Points for Quad; totaling four Points.
1289
1290Point_Array may be read directly from Path with getPoints, or inspected with
1291getPoint, with Iter, or with RawIter.
Cary Clark73fa9722017-08-29 17:36:51 -04001292
1293#Method int getPoints(SkPoint points[], int max) const
1294
Cary Clarkab2621d2018-01-30 10:08:57 -05001295#In Point_Array
1296#Line # returns Point_Array ##
Cary Clark09d80c02018-10-31 12:14:03 -04001297#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001298
1299#Example
1300void draw(SkCanvas* canvas) {
1301 auto debugster = [](const char* prefix, const SkPath& path, SkPoint* points, int max) -> void {
1302 int count = path.getPoints(points, max);
1303 SkDebugf("%s point count: %d ", prefix, count);
1304 for (int i = 0; i < SkTMin(count, max) && points; ++i) {
1305 SkDebugf("(%1.8g,%1.8g) ", points[i].fX, points[i].fY);
1306 }
1307 SkDebugf("\n");
1308 };
1309 SkPath path;
1310 path.lineTo(20, 20);
1311 path.lineTo(-10, -10);
1312 SkPoint points[3];
1313 debugster("no points", path, nullptr, 0);
1314 debugster("zero max", path, points, 0);
1315 debugster("too small", path, points, 2);
1316 debugster("just right", path, points, path.countPoints());
1317}
1318#StdOut
1319no points point count: 3
1320zero max point count: 3
1321too small point count: 3 (0,0) (20,20)
1322just right point count: 3 (0,0) (20,20) (-10,-10)
1323##
1324##
1325
1326#SeeAlso countPoints getPoint
1327##
1328
1329#Method int countPoints() const
1330
Cary Clarkab2621d2018-01-30 10:08:57 -05001331#In Point_Array
1332#Line # returns Point_Array length ##
Cary Clark09d80c02018-10-31 12:14:03 -04001333#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001334
1335#Example
1336void draw(SkCanvas* canvas) {
1337 auto debugster = [](const char* prefix, const SkPath& path) -> void {
1338 SkDebugf("%s point count: %d\n", prefix, path.countPoints());
1339 };
1340 SkPath path;
1341 debugster("empty", path);
1342 path.lineTo(0, 0);
1343 debugster("zero line", path);
1344 path.rewind();
1345 path.moveTo(10, 10);
1346 path.lineTo(20, 20);
1347 debugster("line", path);
1348 path.moveTo(20, 20);
1349 debugster("second move", path);
1350}
1351#StdOut
1352empty point count: 0
1353zero line point count: 2
1354line point count: 2
1355second move point count: 3
1356##
1357##
1358
1359#SeeAlso getPoints
1360##
1361
1362#Method SkPoint getPoint(int index) const
1363
Cary Clarkab2621d2018-01-30 10:08:57 -05001364#In Point_Array
1365#Line # returns entry from Point_Array ##
Cary Clark09d80c02018-10-31 12:14:03 -04001366#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001367
1368#Example
1369void draw(SkCanvas* canvas) {
Cary Clark73fa9722017-08-29 17:36:51 -04001370 SkPath path;
1371 path.lineTo(20, 20);
1372 path.offset(-10, -10);
1373 for (int i= 0; i < path.countPoints(); ++i) {
1374 SkDebugf("point %d: (%1.8g,%1.8g)\n", i, path.getPoint(i).fX, path.getPoint(i).fY);
Cary Clark682c58d2018-05-16 07:07:07 -04001375 }
Cary Clark73fa9722017-08-29 17:36:51 -04001376}
1377#StdOut
1378point 0: (-10,-10)
1379point 1: (10,10)
1380##
1381##
1382
1383#SeeAlso countPoints getPoints
1384##
1385
1386
1387#Subtopic Point_Array ##
1388
1389# ------------------------------------------------------------------------------
Cary Clark8032b982017-07-28 11:04:54 -04001390#Subtopic Verb_Array
Cary Clark08895c42018-02-01 09:37:32 -05001391#Line # line and curve type for points ##
Cary Clark8032b982017-07-28 11:04:54 -04001392
1393Verb_Array always starts with kMove_Verb.
1394If kClose_Verb is not the last entry, it is always followed by kMove_Verb;
1395the quantity of kMove_Verb equals the Contour count.
1396Verb_Array does not include or count kDone_Verb; it is a convenience
1397returned when iterating through Verb_Array.
1398
Cary Clark682c58d2018-05-16 07:07:07 -04001399Verb_Array may be read directly from Path with getVerbs, or inspected with Iter,
Cary Clark8032b982017-07-28 11:04:54 -04001400or with RawIter.
Cary Clark73fa9722017-08-29 17:36:51 -04001401
1402#Method int countVerbs() const
1403
Cary Clarkab2621d2018-01-30 10:08:57 -05001404#In Verb_Array
1405#Line # returns Verb_Array length ##
Cary Clark09d80c02018-10-31 12:14:03 -04001406#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001407
1408#Example
1409SkPath path;
1410SkDebugf("empty verb count: %d\n", path.countVerbs());
1411path.addRoundRect({10, 20, 30, 40}, 5, 5);
1412SkDebugf("round rect verb count: %d\n", path.countVerbs());
1413#StdOut
1414empty verb count: 0
1415round rect verb count: 10
1416##
1417##
1418
1419#SeeAlso getVerbs Iter RawIter
1420
1421##
1422
1423#Method int getVerbs(uint8_t verbs[], int max) const
1424
Cary Clarkab2621d2018-01-30 10:08:57 -05001425#In Verb_Array
1426#Line # returns Verb_Array ##
Cary Clark09d80c02018-10-31 12:14:03 -04001427#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001428
1429#Example
1430void draw(SkCanvas* canvas) {
1431 auto debugster = [](const char* prefix, const SkPath& path, uint8_t* verbs, int max) -> void {
1432 int count = path.getVerbs(verbs, max);
1433 SkDebugf("%s verb count: %d ", prefix, count);
1434 const char* verbStr[] = { "move", "line", "quad", "conic", "cubic", "close" };
1435 for (int i = 0; i < SkTMin(count, max) && verbs; ++i) {
1436 SkDebugf("%s ", verbStr[verbs[i]]);
1437 }
1438 SkDebugf("\n");
1439 };
1440 SkPath path;
1441 path.lineTo(20, 20);
1442 path.lineTo(-10, -10);
1443 uint8_t verbs[3];
1444 debugster("no verbs", path, nullptr, 0);
1445 debugster("zero max", path, verbs, 0);
1446 debugster("too small", path, verbs, 2);
1447 debugster("just right", path, verbs, path.countVerbs());
1448}
1449#StdOut
Cary Clark682c58d2018-05-16 07:07:07 -04001450no verbs verb count: 3
1451zero max verb count: 3
1452too small verb count: 3 move line
1453just right verb count: 3 move line line
Cary Clark73fa9722017-08-29 17:36:51 -04001454##
1455##
1456
1457#SeeAlso countVerbs getPoints Iter RawIter
1458##
Cary Clark8032b982017-07-28 11:04:54 -04001459
1460#Subtopic Verb_Array ##
Cary Clark73fa9722017-08-29 17:36:51 -04001461
1462# ------------------------------------------------------------------------------
1463
1464#Method void swap(SkPath& other)
Cary Clark61313f32018-10-08 14:57:48 -04001465#In Operators
Cary Clarkab2621d2018-01-30 10:08:57 -05001466#Line # exchanges Path pair ##
Cary Clark09d80c02018-10-31 12:14:03 -04001467#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001468
1469#Example
1470SkPath path1, path2;
1471path1.addRect({10, 20, 30, 40});
1472path1.swap(path2);
1473const SkRect& b1 = path1.getBounds();
1474SkDebugf("path1 bounds = %g, %g, %g, %g\n", b1.fLeft, b1.fTop, b1.fRight, b1.fBottom);
1475const SkRect& b2 = path2.getBounds();
1476SkDebugf("path2 bounds = %g, %g, %g, %g\n", b2.fLeft, b2.fTop, b2.fRight, b2.fBottom);
1477#StdOut
1478path1 bounds = 0, 0, 0, 0
1479path2 bounds = 10, 20, 30, 40
1480#StdOut ##
1481##
1482
1483#SeeAlso operator=(const SkPath& path)
1484
1485##
1486
1487# ------------------------------------------------------------------------------
1488
Cary Clark682c58d2018-05-16 07:07:07 -04001489#Method const SkRect& getBounds() const
Cary Clark4855f782018-02-06 09:41:53 -05001490#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001491#Line # returns maximum and minimum of Point_Array ##
Cary Clark09d80c02018-10-31 12:14:03 -04001492#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001493
1494#Example
1495#Description
1496Bounds of upright Circle can be predicted from center and radius.
1497Bounds of rotated Circle includes control Points outside of filled area.
1498##
1499 auto debugster = [](const char* prefix, const SkPath& path) -> void {
1500 const SkRect& bounds = path.getBounds();
Cary Clark682c58d2018-05-16 07:07:07 -04001501 SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix,
Cary Clark73fa9722017-08-29 17:36:51 -04001502 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
1503 };
1504 SkPath path;
1505 debugster("empty", path);
1506 path.addCircle(50, 45, 25);
1507 debugster("circle", path);
1508 SkMatrix matrix;
1509 matrix.setRotate(45, 50, 45);
1510 path.transform(matrix);
1511 debugster("rotated circle", path);
1512#StdOut
1513empty bounds = 0, 0, 0, 0
1514circle bounds = 25, 20, 75, 70
1515rotated circle bounds = 14.6447, 9.64466, 85.3553, 80.3553
1516##
1517##
1518
1519#SeeAlso computeTightBounds updateBoundsCache
1520
1521##
1522
1523# ------------------------------------------------------------------------------
Cary Clark4855f782018-02-06 09:41:53 -05001524#Subtopic Utility
Cary Clark4855f782018-02-06 09:41:53 -05001525#Line # rarely called management functions ##
1526##
Cary Clark73fa9722017-08-29 17:36:51 -04001527
Cary Clark682c58d2018-05-16 07:07:07 -04001528#Method void updateBoundsCache() const
Cary Clark4855f782018-02-06 09:41:53 -05001529#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05001530#Line # refreshes result of getBounds ##
Cary Clark09d80c02018-10-31 12:14:03 -04001531#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001532
1533#Example
1534 double times[2] = { 0, 0 };
1535 for (int i = 0; i < 10000; ++i) {
1536 SkPath path;
1537 for (int j = 1; j < 100; ++ j) {
1538 path.addCircle(50 + j, 45 + j, 25 + j);
1539 }
1540 if (1 & i) {
1541 path.updateBoundsCache();
1542 }
1543 double start = SkTime::GetNSecs();
1544 (void) path.getBounds();
1545 times[1 & i] += SkTime::GetNSecs() - start;
1546 }
1547 SkDebugf("uncached avg: %g ms\n", times[0] * 1e-6);
1548 SkDebugf("cached avg: %g ms\n", times[1] * 1e-6);
1549#StdOut
1550#Volatile
1551uncached avg: 0.18048 ms
1552cached avg: 0.182784 ms
1553##
1554##
1555
1556#SeeAlso getBounds
1557#ToDo the results don't make sense, need to profile to figure this out ##
1558
1559##
1560
1561# ------------------------------------------------------------------------------
1562
1563#Method SkRect computeTightBounds() const
Cary Clark4855f782018-02-06 09:41:53 -05001564#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001565#Line # returns extent of geometry ##
Cary Clark09d80c02018-10-31 12:14:03 -04001566#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001567
1568#Example
1569 auto debugster = [](const char* prefix, const SkPath& path) -> void {
1570 const SkRect& bounds = path.computeTightBounds();
Cary Clark682c58d2018-05-16 07:07:07 -04001571 SkDebugf("%s bounds = %g, %g, %g, %g\n", prefix,
Cary Clark73fa9722017-08-29 17:36:51 -04001572 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
1573 };
1574 SkPath path;
1575 debugster("empty", path);
1576 path.addCircle(50, 45, 25);
1577 debugster("circle", path);
1578 SkMatrix matrix;
1579 matrix.setRotate(45, 50, 45);
1580 path.transform(matrix);
1581 debugster("rotated circle", path);
1582#StdOut
1583empty bounds = 0, 0, 0, 0
1584circle bounds = 25, 20, 75, 70
1585rotated circle bounds = 25, 20, 75, 70
1586##
1587##
1588
1589#SeeAlso getBounds
1590
1591##
1592
1593# ------------------------------------------------------------------------------
1594
1595#Method bool conservativelyContainsRect(const SkRect& rect) const
Cary Clark4855f782018-02-06 09:41:53 -05001596#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05001597#Line # returns true if Rect may be inside ##
Cary Clark09d80c02018-10-31 12:14:03 -04001598#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001599
1600#Example
1601#Height 140
1602#Description
1603Rect is drawn in blue if it is contained by red Path.
1604##
1605void draw(SkCanvas* canvas) {
1606 SkPath path;
1607 path.addRoundRect({10, 20, 54, 120}, 10, 20);
1608 SkRect tests[] = {
1609 { 10, 40, 54, 80 },
1610 { 25, 20, 39, 120 },
1611 { 15, 25, 49, 115 },
1612 { 13, 27, 51, 113 },
1613 };
1614 for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) {
1615 SkPaint paint;
1616 paint.setColor(SK_ColorRED);
1617 canvas->drawPath(path, paint);
1618 bool rectInPath = path.conservativelyContainsRect(tests[i]);
1619 paint.setColor(rectInPath ? SK_ColorBLUE : SK_ColorBLACK);
1620 canvas->drawRect(tests[i], paint);
1621 canvas->translate(64, 0);
1622 }
1623}
1624##
1625
1626#SeeAlso contains Op Rect Convexity
1627
1628##
1629
1630# ------------------------------------------------------------------------------
1631
Mike Reed6a388002018-10-16 13:13:09 -04001632#Method void incReserve(int extraPtCount)
Cary Clark4855f782018-02-06 09:41:53 -05001633#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05001634#Line # reserves space for additional data ##
Cary Clark09d80c02018-10-31 12:14:03 -04001635#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001636
1637#Example
1638#Height 192
1639void draw(SkCanvas* canvas) {
1640 auto addPoly = [](int sides, SkScalar size, SkPath* path) -> void {
1641 path->moveTo(size, 0);
1642 for (int i = 1; i < sides; i++) {
1643 SkScalar c, s = SkScalarSinCos(SK_ScalarPI * 2 * i / sides, &c);
1644 path->lineTo(c * size, s * size);
1645 }
1646 path->close();
1647 };
1648 SkPath path;
1649 path.incReserve(3 + 4 + 5 + 6 + 7 + 8 + 9);
1650 for (int sides = 3; sides < 10; ++sides) {
1651 addPoly(sides, sides, &path);
1652 }
1653 SkMatrix matrix;
1654 matrix.setScale(10, 10, -10, -10);
1655 path.transform(matrix);
1656 SkPaint paint;
1657 paint.setStyle(SkPaint::kStroke_Style);
1658 canvas->drawPath(path, paint);
1659}
1660##
1661
1662#SeeAlso Point_Array
1663
1664##
1665
Cary Clark99ebc422018-09-11 15:26:53 -04001666#Method void shrinkToFit()
1667#In Utility
1668#Line # removes unused reserved space ##
Cary Clark09d80c02018-10-31 12:14:03 -04001669#Populate
Cary Clark99ebc422018-09-11 15:26:53 -04001670
1671#NoExample
Florin Malita0e0f1a72018-09-11 16:16:19 -04001672#Height 192
1673void draw(SkCanvas* canvas) {
1674 SkPath skinnyPath;
1675 skinnyPath.lineTo(100, 100);
1676 skinnyPath.shrinkToFit();
1677
1678 SkDebugf("no wasted Path capacity in my house!\n");
1679}
Cary Clark99ebc422018-09-11 15:26:53 -04001680##
Florin Malita0e0f1a72018-09-11 16:16:19 -04001681
Cary Clark99ebc422018-09-11 15:26:53 -04001682#SeeAlso incReserve
Florin Malita0e0f1a72018-09-11 16:16:19 -04001683
Cary Clark99ebc422018-09-11 15:26:53 -04001684##
1685
Cary Clark73fa9722017-08-29 17:36:51 -04001686# ------------------------------------------------------------------------------
Cary Clark4855f782018-02-06 09:41:53 -05001687#Subtopic Build
Cary Clark4855f782018-02-06 09:41:53 -05001688#Line # adds points and verbs to path ##
1689##
Cary Clark73fa9722017-08-29 17:36:51 -04001690
Cary Clark0251b1b2018-08-15 15:14:55 -04001691#Method SkPath& moveTo(SkScalar x, SkScalar y)
Cary Clark4855f782018-02-06 09:41:53 -05001692#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05001693#Line # starts Contour ##
Cary Clark09d80c02018-10-31 12:14:03 -04001694#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04001695
Cary Clark73fa9722017-08-29 17:36:51 -04001696#Example
1697 #Width 140
1698 #Height 100
1699 void draw(SkCanvas* canvas) {
1700 SkRect rect = { 20, 20, 120, 80 };
1701 SkPath path;
1702 path.addRect(rect);
1703 path.moveTo(rect.fLeft, rect.fTop);
1704 path.lineTo(rect.fRight, rect.fBottom);
1705 path.moveTo(rect.fLeft, rect.fBottom);
1706 path.lineTo(rect.fRight, rect.fTop);
1707 SkPaint paint;
1708 paint.setStyle(SkPaint::kStroke_Style);
1709 canvas->drawPath(path, paint);
1710 }
1711##
1712
1713#SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close()
1714
1715##
1716
Cary Clark0251b1b2018-08-15 15:14:55 -04001717#Method SkPath& moveTo(const SkPoint& p)
Cary Clark09d80c02018-10-31 12:14:03 -04001718#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04001719
Cary Clark73fa9722017-08-29 17:36:51 -04001720#Example
1721 #Width 128
1722 #Height 128
1723void draw(SkCanvas* canvas) {
Cary Clark682c58d2018-05-16 07:07:07 -04001724 SkPoint data[][3] = {{{30,40},{60,60},{90,30}}, {{30,120},{60,100},{90,120}},
Cary Clark73fa9722017-08-29 17:36:51 -04001725 {{60,100},{60,40},{70,30}}, {{60,40},{50,20},{70,30}}};
1726 SkPath path;
1727 for (unsigned i = 0; i < SK_ARRAY_COUNT(data); ++i) {
1728 path.moveTo(data[i][0]);
1729 path.lineTo(data[i][1]);
1730 path.lineTo(data[i][2]);
1731 }
1732 SkPaint paint;
1733 paint.setStyle(SkPaint::kStroke_Style);
1734 canvas->drawPath(path, paint);
1735}
1736##
1737
1738#SeeAlso Contour lineTo rMoveTo quadTo conicTo cubicTo close()
1739
1740##
1741
Cary Clark0251b1b2018-08-15 15:14:55 -04001742#Method SkPath& rMoveTo(SkScalar dx, SkScalar dy)
Cary Clark4855f782018-02-06 09:41:53 -05001743#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05001744#Line # starts Contour relative to Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04001745#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04001746
Cary Clark73fa9722017-08-29 17:36:51 -04001747#Example
1748 #Height 100
1749 SkPath path;
1750 path.addRect({20, 20, 80, 80}, SkPath::kCW_Direction, 2);
1751 path.rMoveTo(25, 2);
1752 SkVector arrow[] = {{0, -4}, {-20, 0}, {0, -3}, {-5, 5}, {5, 5}, {0, -3}, {20, 0}};
1753 for (unsigned i = 0; i < SK_ARRAY_COUNT(arrow); ++i) {
1754 path.rLineTo(arrow[i].fX, arrow[i].fY);
1755 }
1756 SkPaint paint;
1757 canvas->drawPath(path, paint);
1758 SkPoint lastPt;
1759 path.getLastPt(&lastPt);
1760 canvas->drawString("start", lastPt.fX, lastPt.fY, paint);
1761##
1762
1763#SeeAlso Contour lineTo moveTo quadTo conicTo cubicTo close()
1764
1765##
1766
1767# ------------------------------------------------------------------------------
1768
Cary Clark0251b1b2018-08-15 15:14:55 -04001769#Method SkPath& lineTo(SkScalar x, SkScalar y)
Cary Clark4855f782018-02-06 09:41:53 -05001770#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05001771#Line # appends Line ##
Cary Clark09d80c02018-10-31 12:14:03 -04001772#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04001773
Cary Clark73fa9722017-08-29 17:36:51 -04001774#Example
1775#Height 100
1776###$
1777void draw(SkCanvas* canvas) {
1778 SkPaint paint;
1779 paint.setAntiAlias(true);
1780 paint.setTextSize(72);
1781 canvas->drawString("#", 120, 80, paint);
1782 paint.setStyle(SkPaint::kStroke_Style);
1783 paint.setStrokeWidth(5);
1784 SkPath path;
1785 SkPoint hash[] = {{58, 28}, {43, 80}, {37, 45}, {85, 45}};
1786 SkVector offsets[] = {{0, 0}, {17, 0}, {0, 0}, {-5, 17}};
1787 unsigned o = 0;
1788 for (unsigned i = 0; i < SK_ARRAY_COUNT(hash); i += 2) {
1789 for (unsigned j = 0; j < 2; o++, j++) {
1790 path.moveTo(hash[i].fX + offsets[o].fX, hash[i].fY + offsets[o].fY);
1791 path.lineTo(hash[i + 1].fX + offsets[o].fX, hash[i + 1].fY + offsets[o].fY);
1792 }
1793 }
1794 canvas->drawPath(path, paint);
1795}
1796$$$#
1797##
1798
1799#SeeAlso Contour moveTo rLineTo addRect
1800
1801##
1802
1803# ------------------------------------------------------------------------------
1804
Cary Clark0251b1b2018-08-15 15:14:55 -04001805#Method SkPath& lineTo(const SkPoint& p)
Cary Clark09d80c02018-10-31 12:14:03 -04001806#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04001807
Cary Clark73fa9722017-08-29 17:36:51 -04001808#Example
1809#Height 100
1810 SkPath path;
1811 SkVector oxo[] = {{25, 25}, {35, 35}, {25, 35}, {35, 25},
1812 {40, 20}, {40, 80}, {60, 20}, {60, 80},
1813 {20, 40}, {80, 40}, {20, 60}, {80, 60}};
1814 for (unsigned i = 0; i < SK_ARRAY_COUNT(oxo); i += 2) {
1815 path.moveTo(oxo[i]);
1816 path.lineTo(oxo[i + 1]);
1817 }
1818 SkPaint paint;
1819 paint.setStyle(SkPaint::kStroke_Style);
1820 canvas->drawPath(path, paint);
1821##
1822
1823#SeeAlso Contour moveTo rLineTo addRect
1824
1825##
1826
1827# ------------------------------------------------------------------------------
1828
Cary Clark0251b1b2018-08-15 15:14:55 -04001829#Method SkPath& rLineTo(SkScalar dx, SkScalar dy)
Cary Clark4855f782018-02-06 09:41:53 -05001830#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05001831#Line # appends Line relative to Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04001832#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04001833
Cary Clark73fa9722017-08-29 17:36:51 -04001834#Example
1835#Height 128
1836void draw(SkCanvas* canvas) {
1837 SkPaint paint;
1838 paint.setAntiAlias(true);
1839 paint.setStyle(SkPaint::kStroke_Style);
1840 SkPath path;
1841 path.moveTo(10, 98);
1842 SkScalar x = 0, y = 0;
1843 for (int i = 10; i < 100; i += 5) {
1844 x += i * ((i & 2) - 1);
1845 y += i * (((i + 1) & 2) - 1);
1846 path.rLineTo(x, y);
Cary Clark682c58d2018-05-16 07:07:07 -04001847
Cary Clark73fa9722017-08-29 17:36:51 -04001848 }
1849 canvas->drawPath(path, paint);
1850}
1851##
1852
1853#SeeAlso Contour moveTo lineTo addRect
1854
1855##
1856
1857# ------------------------------------------------------------------------------
Cary Clark78de7512018-02-07 07:27:09 -05001858#Subtopic Quad
Cary Clark137b8742018-05-30 09:21:49 -04001859#Alias Quad ##
1860#Alias Quads ##
1861#Alias Quadratic_Bezier ##
1862#Alias Quadratic_Beziers ##
Cary Clark682c58d2018-05-16 07:07:07 -04001863#Line # curve described by second-order polynomial ##
Cary Clark8032b982017-07-28 11:04:54 -04001864
Cary Clark77b3f3a2018-11-07 14:59:03 -05001865Quad describes a Quadratic_Bezier, a second-order curve identical to a section
Cary Clark8032b982017-07-28 11:04:54 -04001866of a parabola. Quad begins at a start Point, curves towards a control Point,
1867and then curves to an end Point.
1868
1869#Example
1870#Height 110
Cary Clark73fa9722017-08-29 17:36:51 -04001871void draw(SkCanvas* canvas) {
1872 SkPaint paint;
1873 paint.setAntiAlias(true);
1874 paint.setStyle(SkPaint::kStroke_Style);
1875 SkPoint quadPts[] = {{20, 90}, {120, 10}, {220, 90}};
1876 canvas->drawLine(quadPts[0], quadPts[1], paint);
1877 canvas->drawLine(quadPts[1], quadPts[2], paint);
1878 SkPath path;
1879 path.moveTo(quadPts[0]);
1880 path.quadTo(quadPts[1], quadPts[2]);
1881 paint.setStrokeWidth(3);
1882 canvas->drawPath(path, paint);
1883}
Cary Clark8032b982017-07-28 11:04:54 -04001884##
1885
1886Quad is a special case of Conic where Conic_Weight is set to one.
1887
1888Quad is always contained by the triangle connecting its three Points. Quad
1889begins tangent to the line between start Point and control Point, and ends
1890tangent to the line between control Point and end Point.
1891
1892#Example
1893#Height 160
Cary Clark73fa9722017-08-29 17:36:51 -04001894void draw(SkCanvas* canvas) {
1895 SkPaint paint;
1896 paint.setAntiAlias(true);
1897 paint.setStyle(SkPaint::kStroke_Style);
1898 SkPoint quadPts[] = {{20, 150}, {120, 10}, {220, 150}};
1899 SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 };
1900 for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
1901 paint.setColor(0x7fffffff & colors[i]);
1902 paint.setStrokeWidth(1);
1903 canvas->drawLine(quadPts[0], quadPts[1], paint);
1904 canvas->drawLine(quadPts[1], quadPts[2], paint);
1905 SkPath path;
1906 path.moveTo(quadPts[0]);
1907 path.quadTo(quadPts[1], quadPts[2]);
1908 paint.setStrokeWidth(3);
1909 paint.setColor(colors[i]);
1910 canvas->drawPath(path, paint);
1911 quadPts[1].fY += 30;
1912 }
Cary Clark8032b982017-07-28 11:04:54 -04001913}
1914##
Cary Clark73fa9722017-08-29 17:36:51 -04001915
Cary Clark0251b1b2018-08-15 15:14:55 -04001916#Method SkPath& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Cary Clark73fa9722017-08-29 17:36:51 -04001917
Cary Clarkab2621d2018-01-30 10:08:57 -05001918#In Quad
1919#Line # appends Quad ##
Cary Clark09d80c02018-10-31 12:14:03 -04001920#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001921
Cary Clark09d80c02018-10-31 12:14:03 -04001922#Example
Cary Clark73fa9722017-08-29 17:36:51 -04001923 void draw(SkCanvas* canvas) {
1924 SkPaint paint;
1925 paint.setAntiAlias(true);
1926 paint.setStyle(SkPaint::kStroke_Style);
1927 SkPath path;
1928 path.moveTo(0, -10);
1929 for (int i = 0; i < 128; i += 16) {
1930 path.quadTo( 10 + i, -10 - i, 10 + i, 0);
1931 path.quadTo( 14 + i, 14 + i, 0, 14 + i);
1932 path.quadTo(-18 - i, 18 + i, -18 - i, 0);
1933 path.quadTo(-22 - i, -22 - i, 0, -22 - i);
1934 }
1935 path.offset(128, 128);
Cary Clark8032b982017-07-28 11:04:54 -04001936 canvas->drawPath(path, paint);
Cary Clark73fa9722017-08-29 17:36:51 -04001937 }
1938 ##
1939
1940 #SeeAlso Contour moveTo conicTo rQuadTo
1941
1942##
1943
Cary Clark0251b1b2018-08-15 15:14:55 -04001944#Method SkPath& quadTo(const SkPoint& p1, const SkPoint& p2)
Cary Clark4855f782018-02-06 09:41:53 -05001945#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05001946#In Quad
Cary Clark09d80c02018-10-31 12:14:03 -04001947#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001948
Cary Clark09d80c02018-10-31 12:14:03 -04001949#Example
Cary Clark73fa9722017-08-29 17:36:51 -04001950 void draw(SkCanvas* canvas) {
1951 SkPaint paint;
1952 paint.setStyle(SkPaint::kStroke_Style);
1953 paint.setAntiAlias(true);
1954 SkPath path;
1955 SkPoint pts[] = {{128, 10}, {10, 214}, {236, 214}};
1956 path.moveTo(pts[1]);
1957 for (int i = 0; i < 3; ++i) {
1958 path.quadTo(pts[i % 3], pts[(i + 2) % 3]);
1959 }
1960 canvas->drawPath(path, paint);
1961 }
1962 ##
1963
1964 #SeeAlso Contour moveTo conicTo rQuadTo
1965
1966##
1967
Cary Clark0251b1b2018-08-15 15:14:55 -04001968#Method SkPath& rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2)
Cary Clark4855f782018-02-06 09:41:53 -05001969#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05001970#In Quad
1971#Line # appends Quad relative to Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04001972#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04001973
Cary Clark09d80c02018-10-31 12:14:03 -04001974#Example
Cary Clark73fa9722017-08-29 17:36:51 -04001975 void draw(SkCanvas* canvas) {
1976 SkPaint paint;
1977 paint.setAntiAlias(true);
1978 SkPath path;
1979 path.moveTo(128, 20);
1980 path.rQuadTo(-6, 10, -7, 10);
1981 for (int i = 1; i < 32; i += 4) {
1982 path.rQuadTo(10 + i, 10 + i, 10 + i * 4, 10);
1983 path.rQuadTo(-10 - i, 10 + i, -10 - (i + 2) * 4, 10);
1984 }
1985 path.quadTo(92, 220, 128, 215);
1986 canvas->drawPath(path, paint);
1987 }
1988 ##
1989
1990 #SeeAlso Contour moveTo conicTo quadTo
1991
1992##
1993
Cary Clark78de7512018-02-07 07:27:09 -05001994#Subtopic Quad ##
Cary Clark73fa9722017-08-29 17:36:51 -04001995
1996# ------------------------------------------------------------------------------
1997
Cary Clark78de7512018-02-07 07:27:09 -05001998#Subtopic Conic
Cary Clark77b3f3a2018-11-07 14:59:03 -05001999#Alias Conic ##
Cary Clark137b8742018-05-30 09:21:49 -04002000#Alias Conics ##
Cary Clark77b3f3a2018-11-07 14:59:03 -05002001#Line # conic section defined by three points and a weight ##
Cary Clark8032b982017-07-28 11:04:54 -04002002
2003Conic describes a conical section: a piece of an ellipse, or a piece of a
Cary Clark682c58d2018-05-16 07:07:07 -04002004parabola, or a piece of a hyperbola. Conic begins at a start Point,
Cary Clark8032b982017-07-28 11:04:54 -04002005curves towards a control Point, and then curves to an end Point. The influence
2006of the control Point is determined by Conic_Weight.
2007
Cary Clark73fa9722017-08-29 17:36:51 -04002008Each Conic in Path adds two Points and one Conic_Weight. Conic_Weights in Path
2009may be inspected with Iter, or with RawIter.
Cary Clark8032b982017-07-28 11:04:54 -04002010
2011#Subtopic Weight
Cary Clark137b8742018-05-30 09:21:49 -04002012#Alias Conic_Weights ##
2013#Alias Weights ##
Cary Clark08895c42018-02-01 09:37:32 -05002014#Line # strength of Conic control Point ##
Cary Clark8032b982017-07-28 11:04:54 -04002015
2016Weight determines both the strength of the control Point and the type of Conic.
Cary Clark682c58d2018-05-16 07:07:07 -04002017Weight varies from zero to infinity. At zero, Weight causes the control Point to
2018have no effect; Conic is identical to a line segment from start Point to end
2019point. If Weight is less than one, Conic follows an elliptical arc.
2020If Weight is exactly one, then Conic is identical to Quad; Conic follows a
2021parabolic arc. If Weight is greater than one, Conic follows a hyperbolic
Cary Clark137b8742018-05-30 09:21:49 -04002022arc. If Weight is infinity, Conic is identical to two line segments, connecting
Cary Clark682c58d2018-05-16 07:07:07 -04002023start Point to control Point, and control Point to end Point.
Cary Clark8032b982017-07-28 11:04:54 -04002024
2025#Example
2026#Description
Cary Clark682c58d2018-05-16 07:07:07 -04002027When Conic_Weight is one, Quad is added to path; the two are identical.
Cary Clark8032b982017-07-28 11:04:54 -04002028##
Cary Clark73fa9722017-08-29 17:36:51 -04002029void draw(SkCanvas* canvas) {
2030 const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
2031 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
2032 SkPath path;
2033 path.conicTo(20, 30, 50, 60, 1);
2034 SkPath::Iter iter(path, false);
2035 SkPath::Verb verb;
2036 do {
2037 SkPoint points[4];
2038 verb = iter.next(points);
2039 SkDebugf("%s ", verbNames[(int) verb]);
2040 for (int i = 0; i < pointCount[(int) verb]; ++i) {
2041 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
2042 }
2043 if (SkPath::kConic_Verb == verb) {
2044 SkDebugf("weight = %g", iter.conicWeight());
2045 }
2046 SkDebugf("\n");
2047 } while (SkPath::kDone_Verb != verb);
Cary Clark8032b982017-07-28 11:04:54 -04002048}
2049#StdOut
Cary Clark682c58d2018-05-16 07:07:07 -04002050move {0, 0},
2051quad {0, 0}, {20, 30}, {50, 60},
2052done
Cary Clark8032b982017-07-28 11:04:54 -04002053##
2054##
2055
2056If weight is less than one, Conic is an elliptical segment.
2057
Cary Clark682c58d2018-05-16 07:07:07 -04002058#Example
Cary Clark8032b982017-07-28 11:04:54 -04002059#Description
Cary Clark2be81cf2018-09-13 12:04:30 -04002060A 90 degree circular arc has the weight #Formula # 1 / sqrt(2) ##.
Cary Clark8032b982017-07-28 11:04:54 -04002061##
Cary Clark73fa9722017-08-29 17:36:51 -04002062void draw(SkCanvas* canvas) {
2063 const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
2064 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
2065 SkPath path;
2066 path.arcTo(20, 0, 20, 20, 20);
2067 SkPath::Iter iter(path, false);
2068 SkPath::Verb verb;
2069 do {
2070 SkPoint points[4];
2071 verb = iter.next(points);
2072 SkDebugf("%s ", verbNames[(int) verb]);
2073 for (int i = 0; i < pointCount[(int) verb]; ++i) {
2074 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
2075 }
2076 if (SkPath::kConic_Verb == verb) {
2077 SkDebugf("weight = %g", iter.conicWeight());
2078 }
2079 SkDebugf("\n");
2080 } while (SkPath::kDone_Verb != verb);
Cary Clark8032b982017-07-28 11:04:54 -04002081}
2082#StdOut
Cary Clark682c58d2018-05-16 07:07:07 -04002083move {0, 0},
Cary Clark73fa9722017-08-29 17:36:51 -04002084conic {0, 0}, {20, 0}, {20, 20}, weight = 0.707107
Cary Clark682c58d2018-05-16 07:07:07 -04002085done
Cary Clark8032b982017-07-28 11:04:54 -04002086##
2087##
2088
Cary Clarkce101242017-09-01 15:51:02 -04002089If weight is greater than one, Conic is a hyperbolic segment. As weight gets large,
Cary Clark8032b982017-07-28 11:04:54 -04002090a hyperbolic segment can be approximated by straight lines connecting the
2091control Point with the end Points.
2092
2093#Example
Cary Clark73fa9722017-08-29 17:36:51 -04002094void draw(SkCanvas* canvas) {
2095 const char* verbNames[] = { "move", "line", "quad", "conic", "cubic", "close", "done" };
2096 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
2097 SkPath path;
2098 path.conicTo(20, 0, 20, 20, SK_ScalarInfinity);
2099 SkPath::Iter iter(path, false);
2100 SkPath::Verb verb;
2101 do {
2102 SkPoint points[4];
2103 verb = iter.next(points);
2104 SkDebugf("%s ", verbNames[(int) verb]);
2105 for (int i = 0; i < pointCount[(int) verb]; ++i) {
2106 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
2107 }
2108 if (SkPath::kConic_Verb == verb) {
2109 SkDebugf("weight = %g", iter.conicWeight());
2110 }
2111 SkDebugf("\n");
2112 } while (SkPath::kDone_Verb != verb);
Cary Clark8032b982017-07-28 11:04:54 -04002113}
2114#StdOut
Cary Clark682c58d2018-05-16 07:07:07 -04002115move {0, 0},
2116line {0, 0}, {20, 0},
2117line {20, 0}, {20, 20},
2118done
Cary Clark8032b982017-07-28 11:04:54 -04002119##
2120##
2121
2122#Subtopic Weight ##
Cary Clark73fa9722017-08-29 17:36:51 -04002123
Cary Clark0251b1b2018-08-15 15:14:55 -04002124#Method SkPath& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
Cary Clark73fa9722017-08-29 17:36:51 -04002125 SkScalar w)
Cary Clarkab2621d2018-01-30 10:08:57 -05002126#In Conic
Cary Clark4855f782018-02-06 09:41:53 -05002127#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002128#Line # appends Conic ##
Cary Clark09d80c02018-10-31 12:14:03 -04002129#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04002130
Cary Clark09d80c02018-10-31 12:14:03 -04002131#Example
Cary Clark8032b982017-07-28 11:04:54 -04002132 #Height 160
Cary Clark73fa9722017-08-29 17:36:51 -04002133 #Description
Cary Clark682c58d2018-05-16 07:07:07 -04002134 As weight increases, curve is pulled towards control point.
Cary Clark73fa9722017-08-29 17:36:51 -04002135 The bottom two curves are elliptical; the next is parabolic; the
2136 top curve is hyperbolic.
2137 ##
2138void draw(SkCanvas* canvas) {
2139 SkPaint paint;
2140 paint.setAntiAlias(true);
2141 paint.setStyle(SkPaint::kStroke_Style);
2142 SkPoint conicPts[] = {{20, 150}, {120, 10}, {220, 150}};
2143 canvas->drawLine(conicPts[0], conicPts[1], paint);
2144 canvas->drawLine(conicPts[1], conicPts[2], paint);
2145 SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 };
2146 paint.setStrokeWidth(3);
2147 SkScalar weight = 0.5f;
2148 for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
2149 SkPath path;
2150 path.moveTo(conicPts[0]);
2151 path.conicTo(conicPts[1], conicPts[2], weight);
2152 paint.setColor(colors[i]);
2153 canvas->drawPath(path, paint);
2154 weight += 0.25f;
2155 }
2156}
2157 ##
2158
2159 #SeeAlso rConicTo arcTo addArc quadTo
2160
2161##
2162
Cary Clark0251b1b2018-08-15 15:14:55 -04002163#Method SkPath& conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w)
Cary Clark4855f782018-02-06 09:41:53 -05002164#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002165#In Conic
Cary Clark09d80c02018-10-31 12:14:03 -04002166#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04002167
Cary Clark09d80c02018-10-31 12:14:03 -04002168#Example
Cary Clark8032b982017-07-28 11:04:54 -04002169 #Height 128
Cary Clark73fa9722017-08-29 17:36:51 -04002170 #Description
2171 Conics and arcs use identical representations. As the arc sweep increases
Cary Clarkce101242017-09-01 15:51:02 -04002172 the Conic_Weight also increases, but remains smaller than one.
Cary Clark73fa9722017-08-29 17:36:51 -04002173 ##
2174void draw(SkCanvas* canvas) {
2175 SkPaint paint;
2176 paint.setAntiAlias(true);
2177 paint.setStyle(SkPaint::kStroke_Style);
2178 SkRect oval = {0, 20, 120, 140};
2179 SkPath path;
2180 for (int i = 0; i < 4; ++i) {
2181 path.moveTo(oval.centerX(), oval.fTop);
2182 path.arcTo(oval, -90, 90 - 20 * i, false);
2183 oval.inset(15, 15);
2184 }
2185 path.offset(100, 0);
2186 SkScalar conicWeights[] = { 0.707107f, 0.819152f, 0.906308f, 0.965926f };
2187 SkPoint conicPts[][3] = { { {40, 20}, {100, 20}, {100, 80} },
2188 { {40, 35}, {71.509f, 35}, {82.286f, 64.6091f} },
2189 { {40, 50}, {53.9892f, 50}, {62.981f, 60.7164f} },
2190 { {40, 65}, {44.0192f, 65}, {47.5f, 67.0096f} } };
2191 for (int i = 0; i < 4; ++i) {
2192 path.moveTo(conicPts[i][0]);
2193 path.conicTo(conicPts[i][1], conicPts[i][2], conicWeights[i]);
2194 }
2195 canvas->drawPath(path, paint);
2196}
2197 ##
2198
2199 #SeeAlso rConicTo arcTo addArc quadTo
2200
2201##
2202
Cary Clark0251b1b2018-08-15 15:14:55 -04002203#Method SkPath& rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
Cary Clark73fa9722017-08-29 17:36:51 -04002204 SkScalar w)
Cary Clark4855f782018-02-06 09:41:53 -05002205#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002206#In Conic
2207#Line # appends Conic relative to Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04002208#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04002209
Cary Clark09d80c02018-10-31 12:14:03 -04002210#Example
Cary Clark73fa9722017-08-29 17:36:51 -04002211 #Height 140
2212 void draw(SkCanvas* canvas) {
2213 SkPaint paint;
2214 paint.setAntiAlias(true);
2215 paint.setStyle(SkPaint::kStroke_Style);
2216 SkPath path;
2217 path.moveTo(20, 80);
2218 path.rConicTo( 60, 0, 60, 60, 0.707107f);
2219 path.rConicTo( 0, -60, 60, -60, 0.707107f);
2220 path.rConicTo(-60, 0, -60, -60, 0.707107f);
2221 path.rConicTo( 0, 60, -60, 60, 0.707107f);
2222 canvas->drawPath(path, paint);
2223 }
2224 ##
2225
2226 #SeeAlso conicTo arcTo addArc quadTo
2227
2228##
2229
Cary Clark78de7512018-02-07 07:27:09 -05002230#Subtopic Conic ##
Cary Clark73fa9722017-08-29 17:36:51 -04002231
2232# ------------------------------------------------------------------------------
Cary Clark78de7512018-02-07 07:27:09 -05002233#Subtopic Cubic
Cary Clark137b8742018-05-30 09:21:49 -04002234#Alias Cubic ##
2235#Alias Cubics ##
2236#Alias Cubic_Bezier ##
2237#Alias Cubic_Beziers ##
Cary Clark682c58d2018-05-16 07:07:07 -04002238#Line # curve described by third-order polynomial ##
Cary Clark8032b982017-07-28 11:04:54 -04002239
Cary Clark682c58d2018-05-16 07:07:07 -04002240Cubic describes a Bezier_Curve segment described by a third-order polynomial.
Cary Clark8032b982017-07-28 11:04:54 -04002241Cubic begins at a start Point, curving towards the first control Point;
2242and curves from the end Point towards the second control Point.
2243
2244#Example
2245#Height 160
Cary Clark73fa9722017-08-29 17:36:51 -04002246void draw(SkCanvas* canvas) {
2247 SkPaint paint;
2248 paint.setAntiAlias(true);
2249 paint.setStyle(SkPaint::kStroke_Style);
2250 SkPoint cubicPts[] = {{20, 150}, {90, 10}, {160, 150}, {230, 10}};
2251 SkColor colors[] = { 0xff88ff00, 0xff0088bb, 0xff6600cc, 0xffbb3377 };
2252 for (unsigned i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
2253 paint.setColor(0x7fffffff & colors[i]);
2254 paint.setStrokeWidth(1);
2255 for (unsigned j = 0; j < 3; ++j) {
2256 canvas->drawLine(cubicPts[j], cubicPts[j + 1], paint);
2257 }
2258 SkPath path;
2259 path.moveTo(cubicPts[0]);
2260 path.cubicTo(cubicPts[1], cubicPts[2], cubicPts[3]);
2261 paint.setStrokeWidth(3);
2262 paint.setColor(colors[i]);
2263 canvas->drawPath(path, paint);
2264 cubicPts[1].fY += 30;
2265 cubicPts[2].fX += 30;
2266 }
Cary Clark8032b982017-07-28 11:04:54 -04002267}
2268##
Cary Clark73fa9722017-08-29 17:36:51 -04002269
Cary Clark0251b1b2018-08-15 15:14:55 -04002270#Method SkPath& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
Cary Clark73fa9722017-08-29 17:36:51 -04002271 SkScalar x3, SkScalar y3)
Cary Clark4855f782018-02-06 09:41:53 -05002272#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002273#In Cubic
2274#Line # appends Cubic ##
Cary Clark09d80c02018-10-31 12:14:03 -04002275#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002276
Cary Clark73fa9722017-08-29 17:36:51 -04002277#Example
2278void draw(SkCanvas* canvas) {
2279 SkPaint paint;
2280 paint.setAntiAlias(true);
2281 paint.setStyle(SkPaint::kStroke_Style);
2282 SkPath path;
2283 path.moveTo(0, -10);
2284 for (int i = 0; i < 128; i += 16) {
2285 SkScalar c = i * 0.5f;
2286 path.cubicTo( 10 + c, -10 - i, 10 + i, -10 - c, 10 + i, 0);
2287 path.cubicTo( 14 + i, 14 + c, 14 + c, 14 + i, 0, 14 + i);
2288 path.cubicTo(-18 - c, 18 + i, -18 - i, 18 + c, -18 - i, 0);
2289 path.cubicTo(-22 - i, -22 - c, -22 - c, -22 - i, 0, -22 - i);
2290 }
2291 path.offset(128, 128);
2292 canvas->drawPath(path, paint);
2293}
2294##
2295
2296#SeeAlso Contour moveTo rCubicTo quadTo
2297
2298##
2299
2300# ------------------------------------------------------------------------------
2301
Cary Clark0251b1b2018-08-15 15:14:55 -04002302#Method SkPath& cubicTo(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3)
Cary Clark73fa9722017-08-29 17:36:51 -04002303
Cary Clark4855f782018-02-06 09:41:53 -05002304#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002305#In Cubic
Cary Clark09d80c02018-10-31 12:14:03 -04002306#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002307
Cary Clark73fa9722017-08-29 17:36:51 -04002308#Example
2309#Height 84
2310 SkPaint paint;
2311 paint.setAntiAlias(true);
2312 paint.setStyle(SkPaint::kStroke_Style);
2313 SkPoint pts[] = { {20, 20}, {300, 80}, {-140, 90}, {220, 10} };
2314 SkPath path;
2315 path.moveTo(pts[0]);
2316 path.cubicTo(pts[1], pts[2], pts[3]);
2317 canvas->drawPath(path, paint);
2318##
2319
2320#SeeAlso Contour moveTo rCubicTo quadTo
2321
2322##
2323
2324# ------------------------------------------------------------------------------
2325
Cary Clark82456492018-10-31 10:54:50 -04002326#Method SkPath& rCubicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
2327 SkScalar dx3, SkScalar dy3)
Cary Clark4855f782018-02-06 09:41:53 -05002328#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002329#In Cubic
2330#Line # appends Cubic relative to Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04002331#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002332
Cary Clark73fa9722017-08-29 17:36:51 -04002333#Example
2334 void draw(SkCanvas* canvas) {
2335 SkPaint paint;
2336 paint.setAntiAlias(true);
2337 paint.setStyle(SkPaint::kStroke_Style);
2338 SkPath path;
2339 path.moveTo(24, 108);
2340 for (int i = 0; i < 16; i++) {
2341 SkScalar sx, sy;
2342 sx = SkScalarSinCos(i * SK_ScalarPI / 8, &sy);
2343 path.rCubicTo(40 * sx, 4 * sy, 4 * sx, 40 * sy, 40 * sx, 40 * sy);
2344 }
2345 canvas->drawPath(path, paint);
2346 }
2347##
2348
2349#SeeAlso Contour moveTo cubicTo quadTo
2350
2351##
2352
Cary Clark78de7512018-02-07 07:27:09 -05002353#Subtopic Cubic ##
Cary Clark73fa9722017-08-29 17:36:51 -04002354
2355# ------------------------------------------------------------------------------
2356
Cary Clark08895c42018-02-01 09:37:32 -05002357#Subtopic Arc
2358#Line # part of Oval or Circle ##
Cary Clark73fa9722017-08-29 17:36:51 -04002359Arc can be constructed in a number of ways. Arc may be described by part of Oval and angles,
2360by start point and end point, and by radius and tangent lines. Each construction has advantages,
2361and some constructions correspond to Arc drawing in graphics standards.
2362
2363All Arc draws are implemented by one or more Conic draws. When Conic_Weight is less than one,
2364Conic describes an Arc of some Oval or Circle.
2365
2366arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo)
2367describes Arc as a piece of Oval, beginning at start angle, sweeping clockwise or counterclockwise,
Cary Clark682c58d2018-05-16 07:07:07 -04002368which may continue Contour or start a new one. This construction is similar to PostScript and
Cary Clark77b3f3a2018-11-07 14:59:03 -05002369HTML_Canvas arcs. Variation addArc always starts new Contour. SkCanvas::drawArc draws without
Cary Clark73fa9722017-08-29 17:36:51 -04002370requiring Path.
2371
2372arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius)
Cary Clark77b3f3a2018-11-07 14:59:03 -05002373describes Arc as tangent to the line segment from last Point added to Path to (x1, y1); and tangent
2374to the line segment from (x1, y1) to (x2, y2). This construction is similar to PostScript and
Cary Clark73fa9722017-08-29 17:36:51 -04002375HTML_Canvas arcs.
2376
2377arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
Cary Clark682c58d2018-05-16 07:07:07 -04002378 SkScalar x, SkScalar y)
Cary Clark73fa9722017-08-29 17:36:51 -04002379describes Arc as part of Oval with radii (rx, ry), beginning at
2380last Point added to Path and ending at (x, y). More than one Arc satisfies this criteria,
2381so additional values choose a single solution. This construction is similar to SVG arcs.
2382
2383conicTo describes Arc of less than 180 degrees as a pair of tangent lines and Conic_Weight.
2384conicTo can represent any Arc with a sweep less than 180 degrees at any rotation. All arcTo
Cary Clark682c58d2018-05-16 07:07:07 -04002385constructions are converted to Conic data when added to Path.
Cary Clark73fa9722017-08-29 17:36:51 -04002386
Cary Clark153e76d2018-08-28 11:48:28 -04002387#ToDo example is spaced correctly on fiddle but spacing is too wide on pc
Cary Clark73fa9722017-08-29 17:36:51 -04002388##
2389
Cary Clark153e76d2018-08-28 11:48:28 -04002390#Illustration
2391
Cary Clark73fa9722017-08-29 17:36:51 -04002392#List
2393# <sup>1</sup> arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) ##
Cary Clark77b3f3a2018-11-07 14:59:03 -05002394# <sup>2</sup> parameter adds move to first point ##
Cary Clark5081eed2018-01-22 07:55:48 -05002395# <sup>3</sup> start angle must be multiple of 90 degrees ##
Cary Clark73fa9722017-08-29 17:36:51 -04002396# <sup>4</sup> arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) ##
2397# <sup>5</sup> arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
2398 Direction sweep, SkScalar x, SkScalar y) ##
2399#List ##
Cary Clark73fa9722017-08-29 17:36:51 -04002400
2401#Example
2402#Height 128
Cary Clark73fa9722017-08-29 17:36:51 -04002403void draw(SkCanvas* canvas) {
2404 SkRect oval = {8, 8, 56, 56};
2405 SkPaint ovalPaint;
2406 ovalPaint.setAntiAlias(true);
2407 SkPaint textPaint(ovalPaint);
2408 ovalPaint.setStyle(SkPaint::kStroke_Style);
2409 SkPaint arcPaint(ovalPaint);
2410 arcPaint.setStrokeWidth(5);
2411 arcPaint.setColor(SK_ColorBLUE);
2412 canvas->translate(-64, 0);
2413 for (char arcStyle = '1'; arcStyle <= '6'; ++arcStyle) {
2414 '4' == arcStyle ? canvas->translate(-96, 55) : canvas->translate(64, 0);
2415 canvas->drawText(&arcStyle, 1, 30, 36, textPaint);
2416 canvas->drawOval(oval, ovalPaint);
2417 SkPath path;
2418 path.moveTo({56, 32});
2419 switch (arcStyle) {
2420 case '1':
2421 path.arcTo(oval, 0, 90, false);
2422 break;
2423 case '2':
2424 canvas->drawArc(oval, 0, 90, false, arcPaint);
2425 continue;
2426 case '3':
2427 path.addArc(oval, 0, 90);
2428 break;
2429 case '4':
2430 path.arcTo({56, 56}, {32, 56}, 24);
2431 break;
2432 case '5':
2433 path.arcTo({24, 24}, 0, SkPath::kSmall_ArcSize, SkPath::kCW_Direction, {32, 56});
2434 break;
2435 case '6':
2436 path.conicTo({56, 56}, {32, 56}, SK_ScalarRoot2Over2);
2437 break;
2438 }
2439 canvas->drawPath(path, arcPaint);
2440 }
2441}
2442#Example ##
2443
Cary Clark153e76d2018-08-28 11:48:28 -04002444In the example above:
2445#List
2446# 1 describes an arc from an oval, a starting angle, and a sweep angle. ##
2447# 2 is similar to 1, but does not require building a path to draw. ##
2448# 3 is similar to 1, but always begins new Contour. ##
2449# 4 describes an arc from a pair of tangent lines and a radius. ##
2450# 5 describes an arc from Oval center, arc start Point and arc end Point. ##
2451# 6 describes an arc from a pair of tangent lines and a Conic_Weight. ##
2452#List ##
Cary Clark73fa9722017-08-29 17:36:51 -04002453
Cary Clark0251b1b2018-08-15 15:14:55 -04002454#Method SkPath& arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo)
Cary Clark4855f782018-02-06 09:41:53 -05002455#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002456#In Arc
2457#Line # appends Arc ##
Cary Clark09d80c02018-10-31 12:14:03 -04002458#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002459
Cary Clark73fa9722017-08-29 17:36:51 -04002460#Example
2461#Height 200
2462#Description
2463arcTo continues a previous contour when forceMoveTo is false and when Path
2464is not empty.
2465##
2466void draw(SkCanvas* canvas) {
2467 SkPaint paint;
2468 SkPath path;
2469 paint.setStyle(SkPaint::kStroke_Style);
2470 paint.setStrokeWidth(4);
2471 path.moveTo(0, 0);
2472 path.arcTo({20, 20, 120, 120}, -90, 90, false);
2473 canvas->drawPath(path, paint);
2474 path.rewind();
2475 path.arcTo({120, 20, 220, 120}, -90, 90, false);
2476 canvas->drawPath(path, paint);
2477 path.rewind();
2478 path.moveTo(0, 0);
2479 path.arcTo({20, 120, 120, 220}, -90, 90, true);
2480 canvas->drawPath(path, paint);
2481}
2482##
2483
2484#SeeAlso addArc SkCanvas::drawArc conicTo
2485
2486##
2487
2488# ------------------------------------------------------------------------------
2489
Cary Clark0251b1b2018-08-15 15:14:55 -04002490#Method SkPath& arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius)
Cary Clark4855f782018-02-06 09:41:53 -05002491#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002492#In Arc
Cary Clark09d80c02018-10-31 12:14:03 -04002493#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04002494
2495#Example
2496#Height 226
2497void draw(SkCanvas* canvas) {
2498 SkPaint tangentPaint;
2499 tangentPaint.setAntiAlias(true);
2500 SkPaint textPaint(tangentPaint);
2501 tangentPaint.setStyle(SkPaint::kStroke_Style);
2502 tangentPaint.setColor(SK_ColorGRAY);
2503 SkPaint arcPaint(tangentPaint);
2504 arcPaint.setStrokeWidth(5);
2505 arcPaint.setColor(SK_ColorBLUE);
2506 SkPath path;
2507 SkPoint pts[] = { {56, 20}, {200, 20}, {90, 190} };
2508 SkScalar radius = 50;
2509 path.moveTo(pts[0]);
2510 path.arcTo(pts[1], pts[2], radius);
2511 canvas->drawLine(pts[0], pts[1], tangentPaint);
2512 canvas->drawLine(pts[1], pts[2], tangentPaint);
2513 SkPoint lastPt;
2514 (void) path.getLastPt(&lastPt);
2515 SkVector radial = pts[2] - pts[1];
2516 radial.setLength(radius);
2517 SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX };
2518 canvas->drawCircle(center, radius, tangentPaint);
2519 canvas->drawLine(lastPt, center, tangentPaint);
2520 radial = pts[1] - pts[0];
2521 radial.setLength(radius);
2522 SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX };
2523 canvas->drawLine(center, arcStart, tangentPaint);
2524 canvas->drawPath(path, arcPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002525 canvas->drawString("(x0, y0)", pts[0].fX - 5, pts[0].fY, textPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002526 canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002527 canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002528 canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint);
2529 canvas->drawString("radius", center.fX - 3, center.fY - 16, textPaint);
2530}
2531##
2532
Cary Clark73fa9722017-08-29 17:36:51 -04002533#Example
2534#Height 128
2535void draw(SkCanvas* canvas) {
2536 SkPaint tangentPaint;
2537 tangentPaint.setAntiAlias(true);
2538 SkPaint textPaint(tangentPaint);
2539 tangentPaint.setStyle(SkPaint::kStroke_Style);
2540 tangentPaint.setColor(SK_ColorGRAY);
2541 SkPaint arcPaint(tangentPaint);
2542 arcPaint.setStrokeWidth(5);
2543 arcPaint.setColor(SK_ColorBLUE);
2544 SkPath path;
2545 SkPoint pts[] = { {156, 20}, {200, 20}, {170, 50} };
2546 SkScalar radius = 50;
2547 path.moveTo(pts[0]);
2548 path.arcTo(pts[1], pts[2], radius);
2549 canvas->drawLine(pts[0], pts[1], tangentPaint);
2550 canvas->drawLine(pts[1], pts[2], tangentPaint);
2551 SkPoint lastPt;
2552 (void) path.getLastPt(&lastPt);
2553 SkVector radial = pts[2] - pts[1];
2554 radial.setLength(radius);
2555 SkPoint center = { lastPt.fX - radial.fY, lastPt.fY + radial.fX };
2556 canvas->drawLine(lastPt, center, tangentPaint);
2557 radial = pts[1] - pts[0];
2558 radial.setLength(radius);
2559 SkPoint arcStart = { center.fX + radial.fY, center.fY - radial.fX };
2560 canvas->drawLine(center, arcStart, tangentPaint);
2561 canvas->drawPath(path, arcPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002562 canvas->drawString("(x0, y0)", pts[0].fX, pts[0].fY - 7, textPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002563 canvas->drawString("(x1, y1)", pts[1].fX + 5, pts[1].fY, textPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002564 canvas->drawString("(x2, y2)", pts[2].fX, pts[2].fY + 15, textPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04002565 canvas->drawString("radius", center.fX + 15, center.fY + 25, textPaint);
2566 canvas->drawString("radius", center.fX - 5, center.fY - 20, textPaint);
2567}
2568##
2569
Cary Clark73fa9722017-08-29 17:36:51 -04002570#Example
2571#Description
2572arcTo is represented by Line and circular Conic in Path.
2573##
2574void draw(SkCanvas* canvas) {
2575 SkPath path;
2576 path.moveTo({156, 20});
2577 path.arcTo(200, 20, 170, 50, 50);
2578 SkPath::Iter iter(path, false);
2579 SkPoint p[4];
2580 SkPath::Verb verb;
2581 while (SkPath::kDone_Verb != (verb = iter.next(p))) {
2582 switch (verb) {
2583 case SkPath::kMove_Verb:
2584 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY);
2585 break;
2586 case SkPath::kLine_Verb:
2587 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY);
2588 break;
2589 case SkPath::kConic_Verb:
2590 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n",
2591 p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight());
2592 break;
2593 default:
2594 SkDebugf("unexpected verb\n");
2595 }
2596 }
2597}
2598#StdOut
2599move to (156,20)
2600line (156,20),(79.2893,20)
2601conic (79.2893,20),(200,20),(114.645,105.355) weight 0.382683
2602##
2603##
2604
Cary Clark682c58d2018-05-16 07:07:07 -04002605#SeeAlso conicTo
Cary Clark73fa9722017-08-29 17:36:51 -04002606
2607##
2608
2609# ------------------------------------------------------------------------------
2610
Cary Clark0251b1b2018-08-15 15:14:55 -04002611#Method SkPath& arcTo(const SkPoint p1, const SkPoint p2, SkScalar radius)
Cary Clark4855f782018-02-06 09:41:53 -05002612#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002613#In Arc
Cary Clark09d80c02018-10-31 12:14:03 -04002614#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002615
Cary Clark73fa9722017-08-29 17:36:51 -04002616#Example
2617#Description
2618Because tangent lines are parallel, arcTo appends line from last Path Point to
2619p1, but does not append a circular Conic.
2620##
2621void draw(SkCanvas* canvas) {
2622 SkPath path;
2623 path.moveTo({156, 20});
2624 path.arcTo({200, 20}, {170, 20}, 50);
2625 SkPath::Iter iter(path, false);
2626 SkPoint p[4];
2627 SkPath::Verb verb;
2628 while (SkPath::kDone_Verb != (verb = iter.next(p))) {
2629 switch (verb) {
2630 case SkPath::kMove_Verb:
2631 SkDebugf("move to (%g,%g)\n", p[0].fX, p[0].fY);
2632 break;
2633 case SkPath::kLine_Verb:
2634 SkDebugf("line (%g,%g),(%g,%g)\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY);
2635 break;
2636 case SkPath::kConic_Verb:
2637 SkDebugf("conic (%g,%g),(%g,%g),(%g,%g) weight %g\n",
2638 p[0].fX, p[0].fY, p[1].fX, p[1].fY, p[2].fX, p[2].fY, iter.conicWeight());
2639 break;
2640 default:
2641 SkDebugf("unexpected verb\n");
2642 }
2643 }
2644}
2645#StdOut
2646move to (156,20)
2647line (156,20),(200,20)
2648##
2649##
2650
Cary Clark682c58d2018-05-16 07:07:07 -04002651#SeeAlso conicTo
Cary Clark73fa9722017-08-29 17:36:51 -04002652
2653##
2654
2655# ------------------------------------------------------------------------------
2656
2657#Enum ArcSize
Cary Clark08895c42018-02-01 09:37:32 -05002658#Line # used by arcTo variation ##
Cary Clark73fa9722017-08-29 17:36:51 -04002659
2660#Code
2661 enum ArcSize {
Cary Clark682c58d2018-05-16 07:07:07 -04002662 kSmall_ArcSize,
2663 kLarge_ArcSize,
Cary Clark73fa9722017-08-29 17:36:51 -04002664 };
2665##
2666
Cary Clark77b3f3a2018-11-07 14:59:03 -05002667Four axis-aligned Ovals with the same height and width intersect a pair of Points.
2668ArcSize and Direction select one of the four Ovals, by choosing the larger or smaller
2669arc between the Points; and by choosing the arc Direction, clockwise
2670or counterclockwise.
Cary Clark73fa9722017-08-29 17:36:51 -04002671
2672#Const kSmall_ArcSize 0
Cary Clark682c58d2018-05-16 07:07:07 -04002673#Line # smaller of Arc pair ##
Cary Clark73fa9722017-08-29 17:36:51 -04002674##
2675#Const kLarge_ArcSize 1
Cary Clark682c58d2018-05-16 07:07:07 -04002676#Line # larger of Arc pair ##
Cary Clark73fa9722017-08-29 17:36:51 -04002677##
2678
2679#Example
2680#Height 160
2681#Description
2682Arc begins at top of Oval pair and ends at bottom. Arc can take four routes to get there.
2683Two routes are large, and two routes are counterclockwise. The one route both large
2684and counterclockwise is blue.
2685##
2686void draw(SkCanvas* canvas) {
2687 SkPaint paint;
2688 paint.setAntiAlias(true);
2689 paint.setStyle(SkPaint::kStroke_Style);
2690 for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
2691 for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) {
2692 SkPath path;
2693 path.moveTo({120, 50});
2694 path.arcTo(70, 40, 30, arcSize, sweep, 156, 100);
2695 if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) {
2696 paint.setColor(SK_ColorBLUE);
2697 paint.setStrokeWidth(3);
2698 }
2699 canvas->drawPath(path, paint);
2700 }
2701 }
2702}
2703##
2704
2705#SeeAlso arcTo Direction
2706
2707##
2708
2709# ------------------------------------------------------------------------------
2710
Cary Clark0251b1b2018-08-15 15:14:55 -04002711#Method SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
Cary Clark73fa9722017-08-29 17:36:51 -04002712 Direction sweep, SkScalar x, SkScalar y)
Cary Clark4855f782018-02-06 09:41:53 -05002713#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002714#In Arc
Cary Clark09d80c02018-10-31 12:14:03 -04002715#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002716
Cary Clark73fa9722017-08-29 17:36:51 -04002717#Example
2718#Height 160
2719void draw(SkCanvas* canvas) {
2720 SkPaint paint;
2721 paint.setAntiAlias(true);
2722 paint.setStyle(SkPaint::kStroke_Style);
2723 for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
2724 for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) {
2725 SkPath path;
2726 path.moveTo({120, 50});
2727 path.arcTo(70, 40, 30, arcSize, sweep, 120.1, 50);
2728 if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) {
2729 paint.setColor(SK_ColorBLUE);
2730 paint.setStrokeWidth(3);
2731 }
2732 canvas->drawPath(path, paint);
2733 }
2734 }
2735}
2736##
2737
2738#SeeAlso rArcTo ArcSize Direction
2739
2740##
2741
2742# ------------------------------------------------------------------------------
2743
Cary Clark0251b1b2018-08-15 15:14:55 -04002744#Method SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
Cary Clark682c58d2018-05-16 07:07:07 -04002745 const SkPoint xy)
Cary Clark4855f782018-02-06 09:41:53 -05002746#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002747#In Arc
Cary Clark09d80c02018-10-31 12:14:03 -04002748#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002749
Cary Clark73fa9722017-08-29 17:36:51 -04002750#Example
2751#Height 108
2752void draw(SkCanvas* canvas) {
2753 SkPaint paint;
2754 SkPath path;
2755 const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}};
2756 for (auto start : starts) {
2757 path.moveTo(start.fX, start.fY);
2758 path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0);
2759 }
2760 canvas->drawPath(path, paint);
2761}
2762##
2763
2764#SeeAlso rArcTo ArcSize Direction
2765
2766##
2767
2768# ------------------------------------------------------------------------------
2769
Cary Clark0251b1b2018-08-15 15:14:55 -04002770#Method SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
Cary Clark73fa9722017-08-29 17:36:51 -04002771 Direction sweep, SkScalar dx, SkScalar dy)
Cary Clark4855f782018-02-06 09:41:53 -05002772#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002773#In Arc
2774#Line # appends Arc relative to Last_Point ##
Cary Clark73fa9722017-08-29 17:36:51 -04002775
Cary Clark80247e52018-07-11 16:18:41 -04002776Appends Arc to Path, relative to last Path Point. Arc is implemented by one or
Cary Clarkce101242017-09-01 15:51:02 -04002777more Conic, weighted to describe part of Oval with radii (rx, ry) rotated by
Cary Clark77b3f3a2018-11-07 14:59:03 -05002778xAxisRotate degrees. Arc curves from last Path Point to relative end Point
2779(dx, dy), choosing one of four possible routes: clockwise or
Cary Clark73fa9722017-08-29 17:36:51 -04002780counterclockwise, and smaller or larger. If Path is empty, the start Arc Point
2781is (0, 0).
2782
Cary Clarkce101242017-09-01 15:51:02 -04002783Arc sweep is always less than 360 degrees. arcTo appends Line to end Point
2784if either radii are zero, or if last Path Point equals end Point.
2785arcTo scales radii (rx, ry) to fit last Path Point and end Point if both are
2786greater than zero but too small to describe an arc.
Cary Clark73fa9722017-08-29 17:36:51 -04002787
2788arcTo appends up to four Conic curves.
Cary Clarkce101242017-09-01 15:51:02 -04002789arcTo implements the functionality of SVG_Arc, although SVG "sweep-flag" value is
2790opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while
Cary Clark73fa9722017-08-29 17:36:51 -04002791kCW_Direction cast to int is zero.
2792
Cary Clark5538c132018-06-14 12:28:14 -04002793#Param rx radius before x-axis rotation ##
2794#Param ry radius before x-axis rotation ##
Cary Clarkce101242017-09-01 15:51:02 -04002795#Param xAxisRotate x-axis rotation in degrees; positive values are clockwise ##
Cary Clark73fa9722017-08-29 17:36:51 -04002796#Param largeArc chooses smaller or larger Arc ##
2797#Param sweep chooses clockwise or counterclockwise Arc ##
Cary Clark5538c132018-06-14 12:28:14 -04002798#Param dx x-axis offset end of Arc from last Path Point ##
2799#Param dy y-axis offset end of Arc from last Path Point ##
Cary Clark73fa9722017-08-29 17:36:51 -04002800
Cary Clark0251b1b2018-08-15 15:14:55 -04002801#Return reference to Path ##
2802
Cary Clark73fa9722017-08-29 17:36:51 -04002803#Example
2804#Height 108
2805void draw(SkCanvas* canvas) {
2806 SkPaint paint;
2807 SkPath path;
2808 const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}};
2809 for (auto start : starts) {
2810 path.moveTo(start.fX, start.fY);
2811 path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0);
2812 }
2813 canvas->drawPath(path, paint);
2814}
2815##
2816
2817#SeeAlso arcTo ArcSize Direction
2818
2819##
2820
Cary Clark78de7512018-02-07 07:27:09 -05002821#Subtopic Arc ##
Cary Clark73fa9722017-08-29 17:36:51 -04002822
2823# ------------------------------------------------------------------------------
2824
Cary Clark0251b1b2018-08-15 15:14:55 -04002825#Method SkPath& close()
Cary Clark4855f782018-02-06 09:41:53 -05002826#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05002827#Line # makes last Contour a loop ##
Cary Clark09d80c02018-10-31 12:14:03 -04002828#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04002829
Cary Clark73fa9722017-08-29 17:36:51 -04002830#Example
2831void draw(SkCanvas* canvas) {
2832 SkPaint paint;
2833 paint.setStrokeWidth(15);
2834 paint.setStrokeCap(SkPaint::kRound_Cap);
2835 SkPath path;
2836 const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}};
2837 path.addPoly(points, SK_ARRAY_COUNT(points), false);
2838 for (int loop = 0; loop < 2; ++loop) {
2839 for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style,
2840 SkPaint::kStrokeAndFill_Style} ) {
2841 paint.setStyle(style);
2842 canvas->drawPath(path, paint);
2843 canvas->translate(85, 0);
2844 }
2845 path.close();
2846 canvas->translate(-255, 128);
2847 }
2848}
2849##
2850
Cary Clark682c58d2018-05-16 07:07:07 -04002851#SeeAlso
Cary Clark73fa9722017-08-29 17:36:51 -04002852
2853##
2854
2855# ------------------------------------------------------------------------------
2856
Cary Clark682c58d2018-05-16 07:07:07 -04002857#Method static bool IsInverseFillType(FillType fill)
Cary Clark4855f782018-02-06 09:41:53 -05002858#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05002859#Line # returns if Fill_Type represents outside geometry ##
Cary Clark73fa9722017-08-29 17:36:51 -04002860Returns true if fill is inverted and Path with fill represents area outside
2861of its geometric bounds.
2862
2863#Table
2864#Legend
2865# FillType # is inverse ##
2866##
2867# kWinding_FillType # false ##
2868# kEvenOdd_FillType # false ##
2869# kInverseWinding_FillType # true ##
2870# kInverseEvenOdd_FillType # true ##
2871##
2872
2873#Param fill one of: kWinding_FillType, kEvenOdd_FillType,
2874 kInverseWinding_FillType, kInverseEvenOdd_FillType
2875##
2876
2877#Return true if Path fills outside its bounds ##
2878
2879#Example
2880#Function
Cary Clark1a8d7622018-03-05 13:26:16 -05002881###$
Cary Clark73fa9722017-08-29 17:36:51 -04002882#define nameValue(fill) { SkPath::fill, #fill }
2883
Cary Clark1a8d7622018-03-05 13:26:16 -05002884$$$#
Cary Clark73fa9722017-08-29 17:36:51 -04002885##
2886void draw(SkCanvas* canvas) {
2887 struct {
2888 SkPath::FillType fill;
2889 const char* name;
2890 } fills[] = {
2891 nameValue(kWinding_FillType),
2892 nameValue(kEvenOdd_FillType),
2893 nameValue(kInverseWinding_FillType),
2894 nameValue(kInverseEvenOdd_FillType),
2895 };
2896 for (auto fill: fills ) {
2897 SkDebugf("IsInverseFillType(%s) == %s\n", fill.name, SkPath::IsInverseFillType(fill.fill) ?
2898 "true" : "false");
2899 }
2900}
2901#StdOut
2902IsInverseFillType(kWinding_FillType) == false
2903IsInverseFillType(kEvenOdd_FillType) == false
2904IsInverseFillType(kInverseWinding_FillType) == true
2905IsInverseFillType(kInverseEvenOdd_FillType) == true
2906##
2907##
2908
2909#SeeAlso FillType getFillType setFillType ConvertToNonInverseFillType
2910
2911##
2912
2913# ------------------------------------------------------------------------------
2914
Cary Clark682c58d2018-05-16 07:07:07 -04002915#Method static FillType ConvertToNonInverseFillType(FillType fill)
Cary Clark4855f782018-02-06 09:41:53 -05002916#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05002917#Line # returns Fill_Type representing inside geometry ##
Cary Clark73fa9722017-08-29 17:36:51 -04002918Returns equivalent Fill_Type representing Path fill inside its bounds.
Cary Clark73fa9722017-08-29 17:36:51 -04002919
2920#Table
2921#Legend
2922# FillType # inside FillType ##
2923##
2924# kWinding_FillType # kWinding_FillType ##
2925# kEvenOdd_FillType # kEvenOdd_FillType ##
2926# kInverseWinding_FillType # kWinding_FillType ##
2927# kInverseEvenOdd_FillType # kEvenOdd_FillType ##
2928##
2929
2930#Param fill one of: kWinding_FillType, kEvenOdd_FillType,
2931 kInverseWinding_FillType, kInverseEvenOdd_FillType
2932##
2933
2934#Return fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted ##
2935
2936#Example
2937#Function
Cary Clark1a8d7622018-03-05 13:26:16 -05002938###$
Cary Clark73fa9722017-08-29 17:36:51 -04002939#define nameValue(fill) { SkPath::fill, #fill }
2940
Cary Clark1a8d7622018-03-05 13:26:16 -05002941$$$#
Cary Clark73fa9722017-08-29 17:36:51 -04002942##
2943void draw(SkCanvas* canvas) {
2944 struct {
2945 SkPath::FillType fill;
2946 const char* name;
2947 } fills[] = {
2948 nameValue(kWinding_FillType),
2949 nameValue(kEvenOdd_FillType),
2950 nameValue(kInverseWinding_FillType),
2951 nameValue(kInverseEvenOdd_FillType),
2952 };
2953 for (unsigned i = 0; i < SK_ARRAY_COUNT(fills); ++i) {
2954 if (fills[i].fill != (SkPath::FillType) i) {
2955 SkDebugf("fills array order does not match FillType enum order");
2956 break;
Cary Clark682c58d2018-05-16 07:07:07 -04002957 }
Cary Clark73fa9722017-08-29 17:36:51 -04002958 SkDebugf("ConvertToNonInverseFillType(%s) == %s\n", fills[i].name,
2959 fills[(int) SkPath::ConvertToNonInverseFillType(fills[i].fill)].name);
2960 }
2961}
2962#StdOut
2963ConvertToNonInverseFillType(kWinding_FillType) == kWinding_FillType
2964ConvertToNonInverseFillType(kEvenOdd_FillType) == kEvenOdd_FillType
2965ConvertToNonInverseFillType(kInverseWinding_FillType) == kWinding_FillType
2966ConvertToNonInverseFillType(kInverseEvenOdd_FillType) == kEvenOdd_FillType
2967##
2968##
2969
2970#SeeAlso FillType getFillType setFillType IsInverseFillType
2971
2972##
2973
2974# ------------------------------------------------------------------------------
2975
2976#Method static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
2977 SkScalar w, SkPoint pts[], int pow2)
Cary Clark4855f782018-02-06 09:41:53 -05002978#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05002979#Line # approximates Conic with Quad array ##
Cary Clark73fa9722017-08-29 17:36:51 -04002980
2981Approximates Conic with Quad array. Conic is constructed from start Point p0,
Cary Clark682c58d2018-05-16 07:07:07 -04002982control Point p1, end Point p2, and weight w.
Cary Clark73fa9722017-08-29 17:36:51 -04002983Quad array is stored in pts; this storage is supplied by caller.
2984Maximum Quad count is 2 to the pow2.
Cary Clark682c58d2018-05-16 07:07:07 -04002985Every third point in array shares last Point of previous Quad and first Point of
2986next Quad. Maximum pts storage size is given by:
Cary Clark2be81cf2018-09-13 12:04:30 -04002987#Formula # (1 + 2 * (1 << pow2)) * sizeof(SkPoint) ##.
Cary Clark6fc50412017-09-21 12:31:06 -04002988
Cary Clark154beea2017-10-26 07:58:48 -04002989Returns Quad count used the approximation, which may be smaller
Cary Clark73fa9722017-08-29 17:36:51 -04002990than the number requested.
Cary Clark682c58d2018-05-16 07:07:07 -04002991
Cary Clark73fa9722017-08-29 17:36:51 -04002992Conic_Weight determines the amount of influence Conic control point has on the curve.
2993w less than one represents an elliptical section. w greater than one represents
2994a hyperbolic section. w equal to one represents a parabolic section.
2995
2996Two Quad curves are sufficient to approximate an elliptical Conic with a sweep
2997of up to 90 degrees; in this case, set pow2 to one.
2998
2999#Param p0 Conic start Point ##
3000#Param p1 Conic control Point ##
3001#Param p2 Conic end Point ##
3002#Param w Conic weight ##
3003#Param pts storage for Quad array ##
3004#Param pow2 Quad count, as power of two, normally 0 to 5 (1 to 32 Quad curves) ##
3005
Cary Clarka523d2d2017-08-30 08:58:10 -04003006#Return number of Quad curves written to pts ##
Cary Clark73fa9722017-08-29 17:36:51 -04003007
3008#Example
3009#Description
3010A pair of Quad curves are drawn in red on top of the elliptical Conic curve in black.
3011The middle curve is nearly circular. The top-right curve is parabolic, which can
3012be drawn exactly with a single Quad.
3013##
3014void draw(SkCanvas* canvas) {
3015 SkPaint conicPaint;
3016 conicPaint.setAntiAlias(true);
3017 conicPaint.setStyle(SkPaint::kStroke_Style);
3018 SkPaint quadPaint(conicPaint);
3019 quadPaint.setColor(SK_ColorRED);
3020 SkPoint conic[] = { {20, 170}, {80, 170}, {80, 230} };
3021 for (auto weight : { .25f, .5f, .707f, .85f, 1.f } ) {
3022 SkPoint quads[5];
3023 SkPath::ConvertConicToQuads(conic[0], conic[1], conic[2], weight, quads, 1);
3024 SkPath path;
3025 path.moveTo(conic[0]);
3026 path.conicTo(conic[1], conic[2], weight);
3027 canvas->drawPath(path, conicPaint);
3028 path.rewind();
3029 path.moveTo(quads[0]);
3030 path.quadTo(quads[1], quads[2]);
3031 path.quadTo(quads[3], quads[4]);
3032 canvas->drawPath(path, quadPaint);
3033 canvas->translate(50, -50);
3034 }
3035}
3036##
3037
3038#SeeAlso Conic Quad
3039
3040##
3041
3042# ------------------------------------------------------------------------------
3043
3044#Method bool isRect(SkRect* rect, bool* isClosed = nullptr, Direction* direction = nullptr) const
Cary Clark4855f782018-02-06 09:41:53 -05003045#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05003046#Line # returns if describes Rect ##
Cary Clark09d80c02018-10-31 12:14:03 -04003047#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003048
3049#Example
3050#Description
3051After addRect, isRect returns true. Following moveTo permits isRect to return true, but
3052following lineTo does not. addPoly returns true even though rect is not closed, and one
3053side of rect is made up of consecutive line segments.
3054##
3055void draw(SkCanvas* canvas) {
3056 auto debugster = [](const char* prefix, const SkPath& path) -> void {
3057 SkRect rect;
3058 SkPath::Direction direction;
3059 bool isClosed;
Cary Clark682c58d2018-05-16 07:07:07 -04003060 path.isRect(&rect, &isClosed, &direction) ?
Cary Clark73fa9722017-08-29 17:36:51 -04003061 SkDebugf("%s is rect (%g, %g, %g, %g); is %s" "closed; direction %s\n", prefix,
3062 rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, isClosed ? "" : "not ",
3063 SkPath::kCW_Direction == direction ? "CW" : "CCW") :
3064 SkDebugf("%s is not rect\n", prefix);
3065 };
3066 SkPath path;
3067 debugster("empty", path);
3068 path.addRect({10, 20, 30, 40});
3069 debugster("addRect", path);
3070 path.moveTo(60, 70);
3071 debugster("moveTo", path);
3072 path.lineTo(60, 70);
3073 debugster("lineTo", path);
3074 path.reset();
3075 const SkPoint pts[] = { {0, 0}, {0, 80}, {80, 80}, {80, 0}, {40, 0}, {20, 0} };
3076 path.addPoly(pts, SK_ARRAY_COUNT(pts), false);
3077 debugster("addPoly", path);
3078}
3079#StdOut
3080empty is not rect
3081addRect is rect (10, 20, 30, 40); is closed; direction CW
3082moveTo is rect (10, 20, 30, 40); is closed; direction CW
3083lineTo is not rect
3084addPoly is rect (0, 0, 80, 80); is not closed; direction CCW
3085##
3086##
3087
3088#SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isNestedFillRects
3089
3090##
3091
3092# ------------------------------------------------------------------------------
3093
3094#Method bool isNestedFillRects(SkRect rect[2], Direction dirs[2] = nullptr) const
Cary Clark4855f782018-02-06 09:41:53 -05003095#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05003096#Line # returns if describes Rect pair, one inside the other ##
Cary Clark09d80c02018-10-31 12:14:03 -04003097#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003098
3099#Example
3100void draw(SkCanvas* canvas) {
3101 SkPaint paint;
3102 paint.setStyle(SkPaint::kStroke_Style);
3103 paint.setStrokeWidth(5);
3104 SkPath path;
3105 path.addRect({10, 20, 30, 40});
3106 paint.getFillPath(path, &path);
3107 SkRect rects[2];
3108 SkPath::Direction directions[2];
3109 if (path.isNestedFillRects(rects, directions)) {
3110 for (int i = 0; i < 2; ++i) {
3111 SkDebugf("%s (%g, %g, %g, %g); direction %s\n", i ? "inner" : "outer",
3112 rects[i].fLeft, rects[i].fTop, rects[i].fRight, rects[i].fBottom,
3113 SkPath::kCW_Direction == directions[i] ? "CW" : "CCW");
3114 }
3115 } else {
3116 SkDebugf("is not nested rectangles\n");
3117 }
3118}
3119#StdOut
3120outer (7.5, 17.5, 32.5, 42.5); direction CW
3121inner (12.5, 22.5, 27.5, 37.5); direction CCW
3122##
3123##
3124
3125#SeeAlso computeTightBounds conservativelyContainsRect getBounds isConvex isLastContourClosed isRect
3126
3127##
3128
3129# ------------------------------------------------------------------------------
3130
Cary Clark0251b1b2018-08-15 15:14:55 -04003131#Method SkPath& addRect(const SkRect& rect, Direction dir = kCW_Direction)
Cary Clark4855f782018-02-06 09:41:53 -05003132#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003133#Line # adds one Contour containing Rect ##
Cary Clark09d80c02018-10-31 12:14:03 -04003134#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003135
Cary Clark73fa9722017-08-29 17:36:51 -04003136#Example
3137#Description
3138The left Rect dashes starting at the top-left corner, to the right.
3139The right Rect dashes starting at the top-left corner, towards the bottom.
3140##
3141#Height 128
3142void draw(SkCanvas* canvas) {
3143 SkPaint paint;
3144 paint.setStrokeWidth(15);
3145 paint.setStrokeCap(SkPaint::kSquare_Cap);
3146 float intervals[] = { 5, 21.75f };
3147 paint.setStyle(SkPaint::kStroke_Style);
3148 paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
3149 SkPath path;
3150 path.addRect({20, 20, 100, 100}, SkPath::kCW_Direction);
3151 canvas->drawPath(path, paint);
3152 path.rewind();
3153 path.addRect({140, 20, 220, 100}, SkPath::kCCW_Direction);
3154 canvas->drawPath(path, paint);
3155}
3156##
3157
3158#SeeAlso SkCanvas::drawRect Direction
3159
3160##
3161
3162# ------------------------------------------------------------------------------
3163
Cary Clark0251b1b2018-08-15 15:14:55 -04003164#Method SkPath& addRect(const SkRect& rect, Direction dir, unsigned start)
Cary Clark73fa9722017-08-29 17:36:51 -04003165
Cary Clark80247e52018-07-11 16:18:41 -04003166Adds Rect to Path, appending kMove_Verb, three kLine_Verb, and kClose_Verb.
Cary Clark73fa9722017-08-29 17:36:51 -04003167If dir is kCW_Direction, Rect corners are added clockwise; if dir is
3168kCCW_Direction, Rect corners are added counterclockwise.
3169start determines the first corner added.
3170
3171#Table
3172#Legend
3173# start # first corner ##
3174#Legend ##
3175# 0 # top-left ##
3176# 1 # top-right ##
3177# 2 # bottom-right ##
3178# 3 # bottom-left ##
3179#Table ##
3180
3181#Param rect Rect to add as a closed contour ##
3182#Param dir Direction to wind added contour ##
Cary Clarka523d2d2017-08-30 08:58:10 -04003183#Param start initial corner of Rect to add ##
Cary Clark73fa9722017-08-29 17:36:51 -04003184
Cary Clark0251b1b2018-08-15 15:14:55 -04003185#Return reference to Path ##
3186
Cary Clark73fa9722017-08-29 17:36:51 -04003187#Example
3188#Height 128
3189#Description
3190The arrow is just after the initial corner and points towards the next
3191corner appended to Path.
3192##
3193void draw(SkCanvas* canvas) {
3194 const SkPoint arrow[] = { {5, -5}, {15, -5}, {20, 0}, {15, 5}, {5, 5}, {10, 0} };
3195 const SkRect rect = {10, 10, 54, 54};
3196 SkPaint rectPaint;
3197 rectPaint.setAntiAlias(true);
3198 rectPaint.setStyle(SkPaint::kStroke_Style);
3199 SkPaint arrowPaint(rectPaint);
3200 SkPath arrowPath;
3201 arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
3202 arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0,
3203 SkPath1DPathEffect::kRotate_Style));
3204 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
3205 for (unsigned start : { 0, 1, 2, 3 } ) {
3206 SkPath path;
3207 path.addRect(rect, direction, start);
3208 canvas->drawPath(path, rectPaint);
3209 canvas->drawPath(path, arrowPaint);
3210 canvas->translate(64, 0);
3211 }
3212 canvas->translate(-256, 64);
3213 }
3214}
3215##
3216
3217#SeeAlso SkCanvas::drawRect Direction
3218
3219##
3220
3221# ------------------------------------------------------------------------------
3222
Cary Clark0251b1b2018-08-15 15:14:55 -04003223#Method SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
Cary Clark73fa9722017-08-29 17:36:51 -04003224 Direction dir = kCW_Direction)
Cary Clark09d80c02018-10-31 12:14:03 -04003225#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003226
Cary Clark73fa9722017-08-29 17:36:51 -04003227#Example
3228#Description
3229The left Rect dashes start at the top-left corner, and continue to the right.
3230The right Rect dashes start at the top-left corner, and continue down.
3231##
3232#Height 128
3233void draw(SkCanvas* canvas) {
3234 SkPaint paint;
3235 paint.setStrokeWidth(15);
3236 paint.setStrokeCap(SkPaint::kSquare_Cap);
3237 float intervals[] = { 5, 21.75f };
3238 paint.setStyle(SkPaint::kStroke_Style);
3239 paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
3240 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
3241 SkPath path;
3242 path.addRect(20, 20, 100, 100, direction);
3243 canvas->drawPath(path, paint);
3244 canvas->translate(128, 0);
3245 }
3246}
3247##
3248
3249#SeeAlso SkCanvas::drawRect Direction
3250
3251##
3252
3253# ------------------------------------------------------------------------------
3254
Cary Clark0251b1b2018-08-15 15:14:55 -04003255#Method SkPath& addOval(const SkRect& oval, Direction dir = kCW_Direction)
Cary Clark4855f782018-02-06 09:41:53 -05003256#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003257#Line # adds one Contour containing Oval ##
Cary Clark09d80c02018-10-31 12:14:03 -04003258#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003259
Cary Clark73fa9722017-08-29 17:36:51 -04003260#Example
3261#Height 120
3262 SkPaint paint;
3263 SkPath oval;
3264 oval.addOval({20, 20, 160, 80});
3265 canvas->drawPath(oval, paint);
3266##
3267
3268#SeeAlso SkCanvas::drawOval Direction Oval
3269
3270##
3271
3272# ------------------------------------------------------------------------------
3273
Cary Clark0251b1b2018-08-15 15:14:55 -04003274#Method SkPath& addOval(const SkRect& oval, Direction dir, unsigned start)
Cary Clark73fa9722017-08-29 17:36:51 -04003275
Cary Clark80247e52018-07-11 16:18:41 -04003276Adds Oval to Path, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
Cary Clark73fa9722017-08-29 17:36:51 -04003277Oval is upright ellipse bounded by Rect oval with radii equal to half oval width
3278and half oval height. Oval begins at start and continues
3279clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction.
3280
3281#Table
3282#Legend
3283# start # Point ##
3284#Legend ##
3285# 0 # oval.centerX(), oval.fTop ##
3286# 1 # oval.fRight, oval.centerY() ##
3287# 2 # oval.centerX(), oval.fBottom ##
3288# 3 # oval.fLeft, oval.centerY() ##
3289#Table ##
3290
3291#Param oval bounds of ellipse added ##
3292#Param dir Direction to wind ellipse ##
3293#Param start index of initial point of ellipse ##
3294
Cary Clark0251b1b2018-08-15 15:14:55 -04003295#Return reference to Path ##
3296
Cary Clark73fa9722017-08-29 17:36:51 -04003297#Example
3298#Height 160
3299void draw(SkCanvas* canvas) {
3300 const SkPoint arrow[] = { {0, -5}, {10, 0}, {0, 5} };
3301 const SkRect rect = {10, 10, 54, 54};
3302 SkPaint ovalPaint;
3303 ovalPaint.setAntiAlias(true);
3304 SkPaint textPaint(ovalPaint);
Cary Clark73fa9722017-08-29 17:36:51 -04003305 ovalPaint.setStyle(SkPaint::kStroke_Style);
3306 SkPaint arrowPaint(ovalPaint);
3307 SkPath arrowPath;
3308 arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
3309 arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0,
3310 SkPath1DPathEffect::kRotate_Style));
3311 for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
3312 for (unsigned start : { 0, 1, 2, 3 } ) {
3313 SkPath path;
3314 path.addOval(rect, direction, start);
3315 canvas->drawPath(path, ovalPaint);
3316 canvas->drawPath(path, arrowPaint);
3317 canvas->drawText(&"0123"[start], 1, rect.centerX(), rect.centerY() + 5, textPaint);
3318 canvas->translate(64, 0);
3319 }
3320 canvas->translate(-256, 72);
3321 canvas->drawString(SkPath::kCW_Direction == direction ? "clockwise" : "counterclockwise",
3322 128, 0, textPaint);
3323 }
3324}
3325##
3326
3327#SeeAlso SkCanvas::drawOval Direction Oval
3328
3329##
3330
3331# ------------------------------------------------------------------------------
3332
Cary Clark0251b1b2018-08-15 15:14:55 -04003333#Method SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius,
Cary Clark73fa9722017-08-29 17:36:51 -04003334 Direction dir = kCW_Direction)
Cary Clark4855f782018-02-06 09:41:53 -05003335#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003336#Line # adds one Contour containing Circle ##
Cary Clark73fa9722017-08-29 17:36:51 -04003337
Cary Clark80247e52018-07-11 16:18:41 -04003338Adds Circle centered at (x, y) of size radius to Path, appending kMove_Verb,
Cary Clark2be81cf2018-09-13 12:04:30 -04003339four kConic_Verb, and kClose_Verb. Circle begins at: #Formula # (x + radius, y) ##, continuing
Cary Clark154beea2017-10-26 07:58:48 -04003340clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction.
Cary Clark73fa9722017-08-29 17:36:51 -04003341
Cary Clark6fc50412017-09-21 12:31:06 -04003342Has no effect if radius is zero or negative.
Cary Clark73fa9722017-08-29 17:36:51 -04003343
3344#Param x center of Circle ##
3345#Param y center of Circle ##
3346#Param radius distance from center to edge ##
3347#Param dir Direction to wind Circle ##
3348
Cary Clark0251b1b2018-08-15 15:14:55 -04003349#Return reference to Path ##
3350
Cary Clark73fa9722017-08-29 17:36:51 -04003351#Example
3352void draw(SkCanvas* canvas) {
3353 SkPaint paint;
3354 paint.setAntiAlias(true);
3355 paint.setStyle(SkPaint::kStroke_Style);
3356 paint.setStrokeWidth(10);
3357 for (int size = 10; size < 300; size += 20) {
3358 SkPath path;
3359 path.addCircle(128, 128, size, SkPath::kCW_Direction);
3360 canvas->drawPath(path, paint);
3361 }
3362}
3363##
3364
3365#SeeAlso SkCanvas::drawCircle Direction Circle
3366
3367##
3368
3369# ------------------------------------------------------------------------------
3370
Cary Clark0251b1b2018-08-15 15:14:55 -04003371#Method SkPath& addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle)
Cary Clark4855f782018-02-06 09:41:53 -05003372#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003373#Line # adds one Contour containing Arc ##
Cary Clark09d80c02018-10-31 12:14:03 -04003374#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003375
Cary Clark73fa9722017-08-29 17:36:51 -04003376#Example
3377#Description
3378The middle row of the left and right columns draw differently from the entries
Cary Clark682c58d2018-05-16 07:07:07 -04003379above and below because sweepAngle is outside of the range of +/-360,
Cary Clark73fa9722017-08-29 17:36:51 -04003380and startAngle modulo 90 is not zero.
3381##
3382void draw(SkCanvas* canvas) {
3383 SkPaint paint;
3384 for (auto start : { 0, 90, 135, 180, 270 } ) {
3385 for (auto sweep : { -450.f, -180.f, -90.f, 90.f, 180.f, 360.1f } ) {
3386 SkPath path;
3387 path.addArc({10, 10, 35, 45}, start, sweep);
3388 canvas->drawPath(path, paint);
3389 canvas->translate(252 / 6, 0);
3390 }
3391 canvas->translate(-252, 255 / 5);
3392 }
3393}
3394##
3395
3396#SeeAlso Arc arcTo SkCanvas::drawArc
3397
3398##
3399
3400# ------------------------------------------------------------------------------
3401
Cary Clark0251b1b2018-08-15 15:14:55 -04003402#Method SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
Cary Clark73fa9722017-08-29 17:36:51 -04003403 Direction dir = kCW_Direction)
Cary Clark4855f782018-02-06 09:41:53 -05003404#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003405#Line # adds one Contour containing Round_Rect with common corner radii ##
Cary Clark09d80c02018-10-31 12:14:03 -04003406#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003407
Cary Clark73fa9722017-08-29 17:36:51 -04003408#Example
3409#Description
3410If either radius is zero, path contains Rect and is drawn red.
3411If sides are only radii, path contains Oval and is drawn blue.
3412All remaining path draws are convex, and are drawn in gray; no
3413paths constructed from addRoundRect are concave, so none are
3414drawn in green.
3415##
3416void draw(SkCanvas* canvas) {
3417 SkPaint paint;
3418 paint.setAntiAlias(true);
3419 for (auto xradius : { 0, 7, 13, 20 } ) {
3420 for (auto yradius : { 0, 9, 18, 40 } ) {
3421 SkPath path;
3422 path.addRoundRect({10, 10, 36, 46}, xradius, yradius);
3423 paint.setColor(path.isRect(nullptr) ? SK_ColorRED : path.isOval(nullptr) ?
3424 SK_ColorBLUE : path.isConvex() ? SK_ColorGRAY : SK_ColorGREEN);
3425 canvas->drawPath(path, paint);
3426 canvas->translate(64, 0);
3427 }
3428 canvas->translate(-256, 64);
3429 }
3430}
3431##
3432
3433#SeeAlso addRRect SkCanvas::drawRoundRect
3434
3435##
3436
3437# ------------------------------------------------------------------------------
3438
Cary Clark0251b1b2018-08-15 15:14:55 -04003439#Method SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[],
Cary Clark73fa9722017-08-29 17:36:51 -04003440 Direction dir = kCW_Direction)
3441
Cary Clark80247e52018-07-11 16:18:41 -04003442Appends Round_Rect to Path, creating a new closed Contour. Round_Rect has bounds
Cary Clark73fa9722017-08-29 17:36:51 -04003443equal to rect; each corner is 90 degrees of an ellipse with radii from the
3444array.
3445
3446#Table
3447#Legend
3448# radii index # location ##
3449#Legend ##
Cary Clark5538c132018-06-14 12:28:14 -04003450# 0 # x-axis radius of top-left corner ##
3451# 1 # y-axis radius of top-left corner ##
3452# 2 # x-axis radius of top-right corner ##
3453# 3 # y-axis radius of top-right corner ##
3454# 4 # x-axis radius of bottom-right corner ##
3455# 5 # y-axis radius of bottom-right corner ##
3456# 6 # x-axis radius of bottom-left corner ##
3457# 7 # y-axis radius of bottom-left corner ##
Cary Clark73fa9722017-08-29 17:36:51 -04003458#Table ##
3459
Cary Clark682c58d2018-05-16 07:07:07 -04003460If dir is kCW_Direction, Round_Rect starts at top-left of the lower-left corner
3461and winds clockwise. If dir is kCCW_Direction, Round_Rect starts at the
Cary Clark73fa9722017-08-29 17:36:51 -04003462bottom-left of the upper-left corner and winds counterclockwise.
3463
Cary Clark682c58d2018-05-16 07:07:07 -04003464If both radii on any side of rect exceed its length, all radii are scaled
Cary Clark73fa9722017-08-29 17:36:51 -04003465uniformly until the corners fit. If either radius of a corner is less than or
3466equal to zero, both are treated as zero.
3467
Cary Clark77b3f3a2018-11-07 14:59:03 -05003468After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect.
Cary Clark73fa9722017-08-29 17:36:51 -04003469
3470#Param rect bounds of Round_Rect ##
3471#Param radii array of 8 SkScalar values, a radius pair for each corner ##
3472#Param dir Direction to wind Round_Rect ##
3473
Cary Clark0251b1b2018-08-15 15:14:55 -04003474#Return reference to Path ##
3475
Cary Clark73fa9722017-08-29 17:36:51 -04003476#Example
3477void draw(SkCanvas* canvas) {
3478 SkPaint paint;
3479 paint.setAntiAlias(true);
3480 SkScalar radii[] = { 80, 100, 0, 0, 40, 60, 0, 0 };
3481 SkPath path;
3482 SkMatrix rotate90;
3483 rotate90.setRotate(90, 128, 128);
3484 for (int i = 0; i < 4; ++i) {
3485 path.addRoundRect({10, 10, 110, 110}, radii);
3486 path.transform(rotate90);
3487 }
3488 canvas->drawPath(path, paint);
3489}
3490##
3491
3492#SeeAlso addRRect SkCanvas::drawRoundRect
3493
3494##
3495
3496# ------------------------------------------------------------------------------
3497
Cary Clark0251b1b2018-08-15 15:14:55 -04003498#Method SkPath& addRRect(const SkRRect& rrect, Direction dir = kCW_Direction)
Cary Clark4855f782018-02-06 09:41:53 -05003499#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003500#Line # adds one Contour containing Round_Rect ##
Cary Clark09d80c02018-10-31 12:14:03 -04003501#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003502
Cary Clark73fa9722017-08-29 17:36:51 -04003503#Example
3504void draw(SkCanvas* canvas) {
3505 SkPaint paint;
3506 paint.setAntiAlias(true);
3507 SkRRect rrect;
3508 SkVector radii[] = {{50, 50}, {0, 0}, {0, 0}, {50, 50}};
3509 rrect.setRectRadii({10, 10, 110, 110}, radii);
3510 SkPath path;
3511 SkMatrix rotate90;
3512 rotate90.setRotate(90, 128, 128);
3513 for (int i = 0; i < 4; ++i) {
3514 path.addRRect(rrect);
3515 path.transform(rotate90);
3516 }
3517 canvas->drawPath(path, paint);
3518}
3519##
3520
3521#SeeAlso addRoundRect SkCanvas::drawRRect
3522
3523##
3524
3525# ------------------------------------------------------------------------------
3526
Cary Clark0251b1b2018-08-15 15:14:55 -04003527#Method SkPath& addRRect(const SkRRect& rrect, Direction dir, unsigned start)
Cary Clark73fa9722017-08-29 17:36:51 -04003528
Cary Clark80247e52018-07-11 16:18:41 -04003529Adds rrect to Path, creating a new closed Contour. If dir is kCW_Direction, rrect
Cary Clark73fa9722017-08-29 17:36:51 -04003530winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise.
3531start determines the first point of rrect to add.
3532
3533#Table
3534#Legend
3535# start # location ##
3536#Legend ##
3537# 0 # right of top-left corner ##
3538# 1 # left of top-right corner ##
3539# 2 # bottom of top-right corner ##
3540# 3 # top of bottom-right corner ##
3541# 4 # left of bottom-right corner ##
3542# 5 # right of bottom-left corner ##
3543# 6 # top of bottom-left corner ##
3544# 7 # bottom of top-left corner ##
3545#Table ##
3546
3547After appending, Path may be empty, or may contain: Rect, Oval, or Round_Rect.
3548
3549#Param rrect bounds and radii of rounded rectangle ##
3550#Param dir Direction to wind Round_Rect ##
Cary Clarka523d2d2017-08-30 08:58:10 -04003551#Param start index of initial point of Round_Rect ##
Cary Clark73fa9722017-08-29 17:36:51 -04003552
Cary Clark0251b1b2018-08-15 15:14:55 -04003553#Return reference to Path ##
3554
Cary Clark73fa9722017-08-29 17:36:51 -04003555#Example
3556void draw(SkCanvas* canvas) {
Mike Reed6a388002018-10-16 13:13:09 -04003557 SkPaint paint;
3558 paint.setAntiAlias(true);
3559 SkRRect rrect;
3560 rrect.setRectXY({40, 40, 215, 215}, 50, 50);
3561 SkPath path;
3562 path.addRRect(rrect);
3563 canvas->drawPath(path, paint);
3564 for (int start = 0; start < 8; ++start) {
3565 SkPath textPath;
3566 textPath.addRRect(rrect, SkPath::kCW_Direction, start);
3567 SkPathMeasure pathMeasure(textPath, false);
3568 SkPoint position;
3569 SkVector tangent;
3570 if (!pathMeasure.getPosTan(0, &position, &tangent)) {
3571 continue;
3572 }
3573 SkRSXform rsxForm = SkRSXform::Make(tangent.fX, tangent.fY,
3574 position.fX + tangent.fY * 5, position.fY - tangent.fX * 5);
3575 canvas->drawTextRSXform(&"01234567"[start], 1, &rsxForm, nullptr, paint);
3576 }
Cary Clark73fa9722017-08-29 17:36:51 -04003577}
3578##
3579
Cary Clark682c58d2018-05-16 07:07:07 -04003580#SeeAlso addRoundRect SkCanvas::drawRRect
Cary Clark73fa9722017-08-29 17:36:51 -04003581
3582##
3583
3584# ------------------------------------------------------------------------------
3585
Cary Clark0251b1b2018-08-15 15:14:55 -04003586#Method SkPath& addPoly(const SkPoint pts[], int count, bool close)
Cary Clark4855f782018-02-06 09:41:53 -05003587#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003588#Line # adds one Contour containing connected lines ##
Cary Clark09d80c02018-10-31 12:14:03 -04003589#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003590
Cary Clark73fa9722017-08-29 17:36:51 -04003591#Example
3592void draw(SkCanvas* canvas) {
3593 SkPaint paint;
3594 paint.setStrokeWidth(15);
3595 paint.setStrokeCap(SkPaint::kRound_Cap);
3596 const SkPoint points[] = {{20, 20}, {70, 20}, {40, 90}};
3597 for (bool close : { false, true } ) {
3598 SkPath path;
3599 path.addPoly(points, SK_ARRAY_COUNT(points), close);
3600 for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style,
3601 SkPaint::kStrokeAndFill_Style} ) {
3602 paint.setStyle(style);
3603 canvas->drawPath(path, paint);
3604 canvas->translate(85, 0);
3605 }
3606 canvas->translate(-255, 128);
3607 }
3608}
3609##
3610
3611#SeeAlso SkCanvas::drawPoints
3612
3613##
3614
Cary Clark0251b1b2018-08-15 15:14:55 -04003615#Method SkPath& addPoly(const std::initializer_list<SkPoint>& list, bool close)
Cary Clark09d80c02018-10-31 12:14:03 -04003616#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003617
3618#Example
3619void draw(SkCanvas* canvas) {
3620 SkPaint paint;
3621 paint.setStrokeWidth(15);
3622 paint.setStrokeCap(SkPaint::kRound_Cap);
3623 for (bool close : { false, true } ) {
3624 SkPath path;
3625 path.addPoly({{20, 20}, {70, 20}, {40, 90}}, close);
3626 for (auto style : {SkPaint::kStroke_Style, SkPaint::kFill_Style,
3627 SkPaint::kStrokeAndFill_Style} ) {
3628 paint.setStyle(style);
3629 canvas->drawPath(path, paint);
3630 canvas->translate(85, 0);
3631 }
3632 canvas->translate(-255, 128);
3633 }
3634}
3635##
3636
3637#SeeAlso SkCanvas::drawPoints
3638
3639##
3640
Cary Clark73fa9722017-08-29 17:36:51 -04003641# ------------------------------------------------------------------------------
3642
3643#Enum AddPathMode
Cary Clark08895c42018-02-01 09:37:32 -05003644#Line # sets addPath options ##
Cary Clark73fa9722017-08-29 17:36:51 -04003645
3646#Code
3647 enum AddPathMode {
Cary Clark682c58d2018-05-16 07:07:07 -04003648 kAppend_AddPathMode,
3649 kExtend_AddPathMode,
Cary Clark73fa9722017-08-29 17:36:51 -04003650 };
3651##
3652
3653AddPathMode chooses how addPath appends. Adding one Path to another can extend
3654the last Contour or start a new Contour.
3655
3656#Const kAppend_AddPathMode
Cary Clark682c58d2018-05-16 07:07:07 -04003657#Line # appended to destination unaltered ##
Cary Clark73fa9722017-08-29 17:36:51 -04003658 Path Verbs, Points, and Conic_Weights are appended to destination unaltered.
3659 Since Path Verb_Array begins with kMove_Verb if src is not empty, this
3660 starts a new Contour.
3661##
3662#Const kExtend_AddPathMode
Cary Clark682c58d2018-05-16 07:07:07 -04003663#Line # add line if prior Contour is not closed ##
Cary Clark73fa9722017-08-29 17:36:51 -04003664 If destination is closed or empty, start a new Contour. If destination
3665 is not empty, add Line from Last_Point to added Path first Point. Skip added
Cary Clark77b3f3a2018-11-07 14:59:03 -05003666 Path initial kMove_Verb, then append remaining Verbs, Points, and Conic_Weights.
Cary Clark73fa9722017-08-29 17:36:51 -04003667##
3668
3669#Example
3670#Description
3671test is built from path, open on the top row, and closed on the bottom row.
3672The left column uses kAppend_AddPathMode; the right uses kExtend_AddPathMode.
3673The top right composition is made up of one contour; the other three have two.
3674##
3675#Height 180
Cary Clark8032b982017-07-28 11:04:54 -04003676 SkPath path, path2;
3677 path.moveTo(20, 20);
3678 path.lineTo(20, 40);
3679 path.lineTo(40, 20);
3680 path2.moveTo(60, 60);
3681 path2.lineTo(80, 60);
3682 path2.lineTo(80, 40);
3683 SkPaint paint;
3684 paint.setStyle(SkPaint::kStroke_Style);
3685 for (int i = 0; i < 2; i++) {
3686 for (auto addPathMode : { SkPath::kAppend_AddPathMode, SkPath::kExtend_AddPathMode } ) {
3687 SkPath test(path);
3688 test.addPath(path2, addPathMode);
3689 canvas->drawPath(test, paint);
3690 canvas->translate(100, 0);
3691 }
3692 canvas->translate(-200, 100);
3693 path.close();
3694 }
Cary Clark73fa9722017-08-29 17:36:51 -04003695##
3696
3697#SeeAlso addPath reverseAddPath
3698
3699##
3700
3701# ------------------------------------------------------------------------------
3702
Cary Clark0251b1b2018-08-15 15:14:55 -04003703#Method SkPath& addPath(const SkPath& src, SkScalar dx, SkScalar dy,
Cary Clark73fa9722017-08-29 17:36:51 -04003704 AddPathMode mode = kAppend_AddPathMode)
Cary Clark4855f782018-02-06 09:41:53 -05003705#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003706#Line # adds contents of Path ##
Cary Clark09d80c02018-10-31 12:14:03 -04003707#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003708
Cary Clark73fa9722017-08-29 17:36:51 -04003709#Example
3710#Height 180
Cary Clark8032b982017-07-28 11:04:54 -04003711 SkPaint paint;
3712 paint.setTextSize(128);
3713 paint.setFakeBoldText(true);
3714 SkPath dest, text;
3715 paint.getTextPath("O", 1, 50, 120, &text);
3716 for (int i = 0; i < 3; i++) {
3717 dest.addPath(text, i * 20, i * 20);
3718 }
3719 Simplify(dest, &dest);
3720 paint.setStyle(SkPaint::kStroke_Style);
3721 paint.setStrokeWidth(3);
3722 canvas->drawPath(dest, paint);
Cary Clark73fa9722017-08-29 17:36:51 -04003723##
3724
Cary Clark4855f782018-02-06 09:41:53 -05003725#SeeAlso AddPathMode offset reverseAddPath
Cary Clark73fa9722017-08-29 17:36:51 -04003726
3727##
3728
3729# ------------------------------------------------------------------------------
3730
Cary Clark0251b1b2018-08-15 15:14:55 -04003731#Method SkPath& addPath(const SkPath& src, AddPathMode mode = kAppend_AddPathMode)
Cary Clark09d80c02018-10-31 12:14:03 -04003732#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003733
Cary Clark73fa9722017-08-29 17:36:51 -04003734#Example
3735#Height 80
Cary Clark8032b982017-07-28 11:04:54 -04003736 SkPaint paint;
3737 paint.setStyle(SkPaint::kStroke_Style);
3738 SkPath dest, path;
3739 path.addOval({-80, 20, 0, 60}, SkPath::kCW_Direction, 1);
3740 for (int i = 0; i < 2; i++) {
3741 dest.addPath(path, SkPath::kExtend_AddPathMode);
3742 dest.offset(100, 0);
3743 }
Cary Clark73fa9722017-08-29 17:36:51 -04003744 canvas->drawPath(dest, paint);
3745##
3746
3747#SeeAlso AddPathMode reverseAddPath
3748
3749##
3750
3751# ------------------------------------------------------------------------------
3752
Cary Clark0251b1b2018-08-15 15:14:55 -04003753#Method SkPath& addPath(const SkPath& src, const SkMatrix& matrix, AddPathMode mode = kAppend_AddPathMode)
Cary Clark09d80c02018-10-31 12:14:03 -04003754#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003755
Cary Clark73fa9722017-08-29 17:36:51 -04003756#Example
3757#Height 160
Cary Clark8032b982017-07-28 11:04:54 -04003758 SkPaint paint;
3759 paint.setStyle(SkPaint::kStroke_Style);
3760 SkPath dest, path;
3761 path.addOval({20, 20, 200, 120}, SkPath::kCW_Direction, 1);
3762 for (int i = 0; i < 6; i++) {
3763 SkMatrix matrix;
3764 matrix.reset();
3765 matrix.setPerspX(i / 400.f);
3766 dest.addPath(path, matrix);
3767 }
3768 canvas->drawPath(dest, paint);
Cary Clark73fa9722017-08-29 17:36:51 -04003769##
3770
Cary Clark4855f782018-02-06 09:41:53 -05003771#SeeAlso AddPathMode transform offset reverseAddPath
Cary Clark73fa9722017-08-29 17:36:51 -04003772
3773##
3774
3775# ------------------------------------------------------------------------------
3776
Cary Clark0251b1b2018-08-15 15:14:55 -04003777#Method SkPath& reverseAddPath(const SkPath& src)
Cary Clark4855f782018-02-06 09:41:53 -05003778#In Build
Cary Clarkab2621d2018-01-30 10:08:57 -05003779#Line # adds contents of Path back to front ##
Cary Clark09d80c02018-10-31 12:14:03 -04003780#Populate
Cary Clark0251b1b2018-08-15 15:14:55 -04003781
Cary Clark73fa9722017-08-29 17:36:51 -04003782#Example
3783#Height 200
Cary Clark8032b982017-07-28 11:04:54 -04003784 SkPath path;
3785 path.moveTo(20, 20);
3786 path.lineTo(20, 40);
3787 path.lineTo(40, 20);
3788 SkPaint paint;
3789 paint.setStyle(SkPaint::kStroke_Style);
3790 for (int i = 0; i < 2; i++) {
3791 SkPath path2;
3792 path2.moveTo(60, 60);
3793 path2.lineTo(80, 60);
3794 path2.lineTo(80, 40);
3795 for (int j = 0; j < 2; j++) {
3796 SkPath test(path);
3797 test.reverseAddPath(path2);
3798 canvas->drawPath(test, paint);
3799 canvas->translate(100, 0);
3800 path2.close();
3801 }
3802 canvas->translate(-200, 100);
3803 path.close();
3804 }
Cary Clark73fa9722017-08-29 17:36:51 -04003805##
3806
Cary Clark4855f782018-02-06 09:41:53 -05003807#SeeAlso AddPathMode transform offset addPath
Cary Clark73fa9722017-08-29 17:36:51 -04003808
3809##
3810
3811# ------------------------------------------------------------------------------
3812
3813#Method void offset(SkScalar dx, SkScalar dy, SkPath* dst) const
Cary Clark4855f782018-02-06 09:41:53 -05003814#In Transform
Cary Clarkab2621d2018-01-30 10:08:57 -05003815#Line # translates Point_Array ##
Cary Clark09d80c02018-10-31 12:14:03 -04003816#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003817
3818#Example
3819#Height 60
Cary Clark8032b982017-07-28 11:04:54 -04003820 SkPath pattern;
3821 pattern.moveTo(20, 20);
3822 pattern.lineTo(20, 40);
3823 pattern.lineTo(40, 20);
3824 SkPaint paint;
3825 paint.setStyle(SkPaint::kStroke_Style);
3826 for (int i = 0; i < 10; i++) {
3827 SkPath path;
3828 pattern.offset(20 * i, 0, &path);
3829 canvas->drawPath(path, paint);
3830 }
Cary Clark73fa9722017-08-29 17:36:51 -04003831##
3832
3833#SeeAlso addPath transform
3834
3835##
3836
3837# ------------------------------------------------------------------------------
Cary Clark4855f782018-02-06 09:41:53 -05003838#Subtopic Transform
Cary Clark4855f782018-02-06 09:41:53 -05003839#Line # modify all points ##
3840##
Cary Clark73fa9722017-08-29 17:36:51 -04003841
Cary Clark682c58d2018-05-16 07:07:07 -04003842#Method void offset(SkScalar dx, SkScalar dy)
Cary Clark4855f782018-02-06 09:41:53 -05003843#In Transform
Cary Clark09d80c02018-10-31 12:14:03 -04003844#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003845
3846#Example
3847#Height 60
Cary Clark8032b982017-07-28 11:04:54 -04003848 SkPath path;
3849 path.moveTo(20, 20);
3850 path.lineTo(20, 40);
3851 path.lineTo(40, 20);
3852 SkPaint paint;
3853 paint.setStyle(SkPaint::kStroke_Style);
3854 for (int i = 0; i < 10; i++) {
3855 canvas->drawPath(path, paint);
3856 path.offset(20, 0);
3857 }
Cary Clark73fa9722017-08-29 17:36:51 -04003858##
3859
3860#SeeAlso addPath transform SkCanvas::translate()
3861
3862##
3863
3864# ------------------------------------------------------------------------------
3865
3866#Method void transform(const SkMatrix& matrix, SkPath* dst) const
Cary Clark4855f782018-02-06 09:41:53 -05003867#In Transform
Cary Clarkab2621d2018-01-30 10:08:57 -05003868#Line # applies Matrix to Point_Array and Weights ##
Cary Clark09d80c02018-10-31 12:14:03 -04003869#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003870
3871#Example
Cary Clark8032b982017-07-28 11:04:54 -04003872#Height 200
3873 SkPath pattern;
3874 pattern.moveTo(100, 100);
3875 pattern.lineTo(100, 20);
3876 pattern.lineTo(20, 100);
3877 SkPaint paint;
3878 paint.setStyle(SkPaint::kStroke_Style);
3879 for (int i = 0; i < 10; i++) {
3880 SkPath path;
3881 SkMatrix matrix;
3882 matrix.setRotate(36 * i, 100, 100);
3883 pattern.transform(matrix, &path);
3884 canvas->drawPath(path, paint);
Cary Clark73fa9722017-08-29 17:36:51 -04003885 }
3886##
3887
3888#SeeAlso addPath offset SkCanvas::concat() SkMatrix
3889
3890##
3891
3892# ------------------------------------------------------------------------------
3893
Cary Clark682c58d2018-05-16 07:07:07 -04003894#Method void transform(const SkMatrix& matrix)
Cary Clark09d80c02018-10-31 12:14:03 -04003895#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003896
3897#Example
Cary Clark8032b982017-07-28 11:04:54 -04003898#Height 200
3899 SkPath path;
3900 path.moveTo(100, 100);
3901 path.quadTo(100, 20, 20, 100);
3902 SkPaint paint;
3903 paint.setStyle(SkPaint::kStroke_Style);
3904 for (int i = 0; i < 10; i++) {
3905 SkMatrix matrix;
3906 matrix.setRotate(36, 100, 100);
3907 path.transform(matrix);
3908 canvas->drawPath(path, paint);
Cary Clark73fa9722017-08-29 17:36:51 -04003909 }
3910##
3911
3912#SeeAlso addPath offset SkCanvas::concat() SkMatrix
3913
3914##
3915
3916# ------------------------------------------------------------------------------
3917
Cary Clark8032b982017-07-28 11:04:54 -04003918#Subtopic Last_Point
Cary Clark08895c42018-02-01 09:37:32 -05003919#Line # final Point in Contour ##
Cary Clark8032b982017-07-28 11:04:54 -04003920
3921Path is defined cumulatively, often by adding a segment to the end of last
3922Contour. Last_Point of Contour is shared as first Point of added Line or Curve.
3923Last_Point can be read and written directly with getLastPt and setLastPt.
3924
Cary Clark73fa9722017-08-29 17:36:51 -04003925#Method bool getLastPt(SkPoint* lastPt) const
Cary Clark4855f782018-02-06 09:41:53 -05003926#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05003927#In Last_Point
3928#Line # returns Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04003929#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003930
Cary Clark09d80c02018-10-31 12:14:03 -04003931#Example
Cary Clark8032b982017-07-28 11:04:54 -04003932 SkPath path;
3933 path.moveTo(100, 100);
3934 path.quadTo(100, 20, 20, 100);
3935 SkMatrix matrix;
3936 matrix.setRotate(36, 100, 100);
3937 path.transform(matrix);
3938 SkPoint last;
3939 path.getLastPt(&last);
Cary Clark73fa9722017-08-29 17:36:51 -04003940 SkDebugf("last point: %g, %g\n", last.fX, last.fY);
3941 #StdOut
3942 last point: 35.2786, 52.9772
Cary Clark682c58d2018-05-16 07:07:07 -04003943 ##
Cary Clark73fa9722017-08-29 17:36:51 -04003944 ##
3945
3946 #SeeAlso setLastPt
3947
3948##
3949
3950#Method void setLastPt(SkScalar x, SkScalar y)
Cary Clark4855f782018-02-06 09:41:53 -05003951#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05003952#In Last_Point
3953#Line # replaces Last_Point ##
Cary Clark09d80c02018-10-31 12:14:03 -04003954#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003955
Cary Clark09d80c02018-10-31 12:14:03 -04003956#Example
Cary Clark73fa9722017-08-29 17:36:51 -04003957 #Height 128
Cary Clark8032b982017-07-28 11:04:54 -04003958 SkPaint paint;
3959 paint.setTextSize(128);
3960 SkPath path;
3961 paint.getTextPath("@", 1, 60, 100, &path);
3962 path.setLastPt(20, 120);
Cary Clark73fa9722017-08-29 17:36:51 -04003963 canvas->drawPath(path, paint);
3964 ##
3965
3966 #SeeAlso getLastPt
3967
3968##
3969
Cary Clark682c58d2018-05-16 07:07:07 -04003970#Method void setLastPt(const SkPoint& p)
Cary Clark09d80c02018-10-31 12:14:03 -04003971#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04003972
Cary Clark09d80c02018-10-31 12:14:03 -04003973#Example
Cary Clark73fa9722017-08-29 17:36:51 -04003974 #Height 128
Cary Clark8032b982017-07-28 11:04:54 -04003975 SkPaint paint;
3976 paint.setTextSize(128);
3977 SkPath path, path2;
3978 paint.getTextPath("A", 1, 60, 100, &path);
3979 paint.getTextPath("Z", 1, 60, 100, &path2);
3980 SkPoint pt, pt2;
3981 path.getLastPt(&pt);
3982 path2.getLastPt(&pt2);
3983 path.setLastPt(pt2);
3984 path2.setLastPt(pt);
3985 canvas->drawPath(path, paint);
3986 canvas->drawPath(path2, paint);
Cary Clark73fa9722017-08-29 17:36:51 -04003987 ##
3988
3989 #SeeAlso getLastPt
3990
3991##
3992
3993#Subtopic Last_Point ##
3994
3995# ------------------------------------------------------------------------------
3996
3997#Enum SegmentMask
Cary Clark08895c42018-02-01 09:37:32 -05003998#Line # returns Verb types in Path ##
Cary Clark73fa9722017-08-29 17:36:51 -04003999
4000#Code
4001 enum SegmentMask {
4002 kLine_SegmentMask = 1 << 0,
4003 kQuad_SegmentMask = 1 << 1,
4004 kConic_SegmentMask = 1 << 2,
4005 kCubic_SegmentMask = 1 << 3,
4006 };
4007##
4008
4009SegmentMask constants correspond to each drawing Verb type in Path; for
4010instance, if Path only contains Lines, only the kLine_SegmentMask bit is set.
4011
Cary Clark4855f782018-02-06 09:41:53 -05004012#Bug 6785
Cary Clark73fa9722017-08-29 17:36:51 -04004013#Const kLine_SegmentMask 1
Cary Clark682c58d2018-05-16 07:07:07 -04004014#Line # contains one or more Lines ##
Cary Clark73fa9722017-08-29 17:36:51 -04004015Set if Verb_Array contains kLine_Verb.
4016##
4017#Const kQuad_SegmentMask 2
Cary Clark682c58d2018-05-16 07:07:07 -04004018#Line # contains one or more Quads ##
Cary Clark73fa9722017-08-29 17:36:51 -04004019Set if Verb_Array contains kQuad_Verb. Note that conicTo may add a Quad.
4020##
4021#Const kConic_SegmentMask 4
Cary Clark682c58d2018-05-16 07:07:07 -04004022#Line # contains one or more Conics ##
Cary Clark73fa9722017-08-29 17:36:51 -04004023Set if Verb_Array contains kConic_Verb.
4024##
4025#Const kCubic_SegmentMask 8
Cary Clark682c58d2018-05-16 07:07:07 -04004026#Line # contains one or more Cubics ##
Cary Clark73fa9722017-08-29 17:36:51 -04004027Set if Verb_Array contains kCubic_Verb.
4028##
4029
4030#Example
4031#Description
4032When conicTo has a weight of one, Quad is added to Path.
4033##
4034 SkPath path;
4035 path.conicTo(10, 10, 20, 30, 1);
Cary Clark682c58d2018-05-16 07:07:07 -04004036 SkDebugf("Path kConic_SegmentMask is %s\n", path.getSegmentMasks() &
Cary Clark73fa9722017-08-29 17:36:51 -04004037 SkPath::kConic_SegmentMask ? "set" : "clear");
Cary Clark682c58d2018-05-16 07:07:07 -04004038 SkDebugf("Path kQuad_SegmentMask is %s\n", path.getSegmentMasks() &
Cary Clark73fa9722017-08-29 17:36:51 -04004039 SkPath::kQuad_SegmentMask ? "set" : "clear");
4040#StdOut
4041Path kConic_SegmentMask is clear
4042Path kQuad_SegmentMask is set
4043##
4044##
4045
4046#SeeAlso getSegmentMasks Verb
4047
4048##
4049
4050# ------------------------------------------------------------------------------
4051
Cary Clark682c58d2018-05-16 07:07:07 -04004052#Method uint32_t getSegmentMasks() const
Cary Clark4855f782018-02-06 09:41:53 -05004053#In Utility
4054#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05004055#Line # returns types in Verb_Array ##
Cary Clark09d80c02018-10-31 12:14:03 -04004056#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004057
4058#Example
4059SkPath path;
4060path.quadTo(20, 30, 40, 50);
4061path.close();
4062const char* masks[] = { "line", "quad", "conic", "cubic" };
4063int index = 0;
4064for (auto mask : { SkPath::kLine_SegmentMask, SkPath::kQuad_SegmentMask,
4065 SkPath::kConic_SegmentMask, SkPath::kCubic_SegmentMask } ) {
4066 if (mask & path.getSegmentMasks()) {
4067 SkDebugf("mask %s set\n", masks[index]);
Cary Clark682c58d2018-05-16 07:07:07 -04004068 }
Cary Clark73fa9722017-08-29 17:36:51 -04004069 ++index;
4070}
4071#StdOut
4072mask quad set
4073##
4074##
4075
4076#SeeAlso getSegmentMasks Verb
4077
4078##
4079
4080# ------------------------------------------------------------------------------
4081
4082#Method bool contains(SkScalar x, SkScalar y) const
Cary Clark4855f782018-02-06 09:41:53 -05004083#In Property
Cary Clarkab2621d2018-01-30 10:08:57 -05004084#Line # returns if Point is in fill area ##
Cary Clark73fa9722017-08-29 17:36:51 -04004085Returns true if the point (x, y) is contained by Path, taking into
Cary Clark682c58d2018-05-16 07:07:07 -04004086account FillType.
Cary Clark73fa9722017-08-29 17:36:51 -04004087
4088#Table
4089#Legend
4090# FillType # contains() returns true if Point is enclosed by ##
4091##
4092# kWinding_FillType # a non-zero sum of Contour Directions. ##
4093# kEvenOdd_FillType # an odd number of Contours. ##
4094# kInverseWinding_FillType # a zero sum of Contour Directions. ##
4095# kInverseEvenOdd_FillType # and even number of Contours. ##
Cary Clark682c58d2018-05-16 07:07:07 -04004096##
Cary Clark73fa9722017-08-29 17:36:51 -04004097
Cary Clark5538c132018-06-14 12:28:14 -04004098#Param x x-axis value of containment test ##
4099#Param y y-axis value of containment test ##
Cary Clark73fa9722017-08-29 17:36:51 -04004100
4101#Return true if Point is in Path ##
4102
4103#Example
4104SkPath path;
4105SkPaint paint;
4106paint.setTextSize(256);
4107paint.getTextPath("&", 1, 30, 220, &path);
4108for (int y = 2; y < 256; y += 9) {
4109 for (int x = 2; x < 256; x += 9) {
4110 int coverage = 0;
4111 for (int iy = -4; iy <= 4; iy += 2) {
4112 for (int ix = -4; ix <= 4; ix += 2) {
4113 coverage += path.contains(x + ix, y + iy);
4114 }
4115 }
4116 paint.setColor(SkColorSetARGB(0x5f, 0xff * coverage / 25, 0, 0xff * (25 - coverage) / 25));
4117 canvas->drawCircle(x, y, 8, paint);
4118 }
4119}
4120##
4121
4122#SeeAlso conservativelyContainsRect Fill_Type Op
4123
4124##
4125
4126# ------------------------------------------------------------------------------
4127
4128#Method void dump(SkWStream* stream, bool forceClose, bool dumpAsHex) const
Cary Clark4855f782018-02-06 09:41:53 -05004129#In Utility
Cary Clark53498e92018-06-28 19:13:56 -04004130#Line # sends text representation to stream ##
Cary Clark09d80c02018-10-31 12:14:03 -04004131#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004132
4133#Example
4134 SkPath path;
4135 path.quadTo(20, 30, 40, 50);
4136 for (bool forceClose : { false, true } ) {
4137 for (bool dumpAsHex : { false, true } ) {
4138 path.dump(nullptr, forceClose, dumpAsHex);
4139 SkDebugf("\n");
4140 }
4141 }
4142#StdOut
4143path.setFillType(SkPath::kWinding_FillType);
4144path.moveTo(0, 0);
4145path.quadTo(20, 30, 40, 50);
4146
4147path.setFillType(SkPath::kWinding_FillType);
4148path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4149path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000)); // 20, 30, 40, 50
4150
4151path.setFillType(SkPath::kWinding_FillType);
4152path.moveTo(0, 0);
4153path.quadTo(20, 30, 40, 50);
4154path.lineTo(0, 0);
4155path.close();
4156
4157path.setFillType(SkPath::kWinding_FillType);
4158path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4159path.quadTo(SkBits2Float(0x41a00000), SkBits2Float(0x41f00000), SkBits2Float(0x42200000), SkBits2Float(0x42480000)); // 20, 30, 40, 50
4160path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4161path.close();
4162##
4163##
4164
Cary Clark53498e92018-06-28 19:13:56 -04004165#SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump()
Cary Clark73fa9722017-08-29 17:36:51 -04004166
4167##
4168
4169# ------------------------------------------------------------------------------
4170
4171#Method void dump() const
Cary Clark09d80c02018-10-31 12:14:03 -04004172#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004173
4174#Example
4175SkPath path, copy;
4176path.lineTo(6.f / 7, 2.f / 3);
4177path.dump();
4178copy.setFillType(SkPath::kWinding_FillType);
4179copy.moveTo(0, 0);
4180copy.lineTo(0.857143f, 0.666667f);
4181SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
4182#StdOut
4183path.setFillType(SkPath::kWinding_FillType);
4184path.moveTo(0, 0);
4185path.lineTo(0.857143f, 0.666667f);
4186path is not equal to copy
4187##
4188##
4189
4190#SeeAlso dumpHex SkRect::dump() SkRRect::dump() SkPathMeasure::dump() writeToMemory
4191
4192##
4193
4194# ------------------------------------------------------------------------------
4195
4196#Method void dumpHex() const
Cary Clark4855f782018-02-06 09:41:53 -05004197#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05004198#Line # sends text representation using hexadecimal to standard output ##
Cary Clarkce101242017-09-01 15:51:02 -04004199Writes text representation of Path to standard output. The representation may be
Cary Clark73fa9722017-08-29 17:36:51 -04004200directly compiled as C++ code. Floating point values are written
4201in hexadecimal to preserve their exact bit pattern. The output reconstructs the
4202original Path.
4203
Cary Clark682c58d2018-05-16 07:07:07 -04004204Use instead of dump() when submitting
4205#A bug reports against Skia # https://bug.skia.org ##
Cary Clark6fc50412017-09-21 12:31:06 -04004206.
Cary Clark73fa9722017-08-29 17:36:51 -04004207
4208#Example
4209SkPath path, copy;
4210path.lineTo(6.f / 7, 2.f / 3);
4211path.dumpHex();
4212copy.setFillType(SkPath::kWinding_FillType);
4213copy.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4214copy.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab)); // 0.857143f, 0.666667f
4215SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
4216#StdOut
4217path.setFillType(SkPath::kWinding_FillType);
4218path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4219path.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab)); // 0.857143f, 0.666667f
4220path is equal to copy
4221##
4222##
4223
Cary Clark186d08f2018-04-03 08:43:27 -04004224#SeeAlso dump SkRect::dumpHex SkRRect::dumpHex writeToMemory
Cary Clark73fa9722017-08-29 17:36:51 -04004225
4226##
4227
4228# ------------------------------------------------------------------------------
4229
4230#Method size_t writeToMemory(void* buffer) const
Cary Clark4855f782018-02-06 09:41:53 -05004231#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05004232#Line # copies data to buffer ##
Cary Clark09d80c02018-10-31 12:14:03 -04004233#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004234
4235#Example
4236void draw(SkCanvas* canvas) {
4237 SkPath path, copy;
4238 path.lineTo(6.f / 7, 2.f / 3);
4239 size_t size = path.writeToMemory(nullptr);
4240 SkTDArray<char> storage;
4241 storage.setCount(size);
4242 path.writeToMemory(storage.begin());
4243 copy.readFromMemory(storage.begin(), size);
4244 SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
4245}
4246#StdOut
4247path is equal to copy
4248##
4249##
4250
4251#SeeAlso serialize readFromMemory dump dumpHex
4252
4253##
4254
4255#Method sk_sp<SkData> serialize() const
Cary Clark4855f782018-02-06 09:41:53 -05004256#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05004257#Line # copies data to buffer ##
Cary Clark09d80c02018-10-31 12:14:03 -04004258#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004259
4260#Example
4261void draw(SkCanvas* canvas) {
4262 SkPath path, copy;
4263 path.lineTo(6.f / 7, 2.f / 3);
4264 sk_sp<SkData> data = path.serialize();
4265 copy.readFromMemory(data->data(), data->size());
4266 SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
4267}
4268#StdOut
4269path is equal to copy
4270##
4271##
4272
4273#SeeAlso writeToMemory readFromMemory dump dumpHex
4274##
4275
4276# ------------------------------------------------------------------------------
4277
4278#Method size_t readFromMemory(const void* buffer, size_t length)
Cary Clark4855f782018-02-06 09:41:53 -05004279#In Utility
Cary Clark682c58d2018-05-16 07:07:07 -04004280#Line # initializes from buffer ##
Cary Clark09d80c02018-10-31 12:14:03 -04004281#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004282
4283#Example
4284void draw(SkCanvas* canvas) {
4285 SkPath path, copy;
4286 path.lineTo(6.f / 7, 2.f / 3);
4287 size_t size = path.writeToMemory(nullptr);
4288 SkTDArray<char> storage;
4289 storage.setCount(size);
4290 path.writeToMemory(storage.begin());
4291 size_t wrongSize = size - 4;
4292 size_t bytesRead = copy.readFromMemory(storage.begin(), wrongSize);
4293 SkDebugf("length = %u; returned by readFromMemory = %u\n", wrongSize, bytesRead);
4294 size_t largerSize = size + 4;
4295 bytesRead = copy.readFromMemory(storage.begin(), largerSize);
4296 SkDebugf("length = %u; returned by readFromMemory = %u\n", largerSize, bytesRead);
4297}
4298#StdOut
Cary Clark682c58d2018-05-16 07:07:07 -04004299length = 32; returned by readFromMemory = 0
Cary Clark06c20f32018-03-20 15:53:27 -04004300length = 40; returned by readFromMemory = 36
Cary Clark73fa9722017-08-29 17:36:51 -04004301##
4302##
4303
4304#SeeAlso writeToMemory
4305
4306##
4307
4308# ------------------------------------------------------------------------------
Cary Clark78de7512018-02-07 07:27:09 -05004309#Subtopic Generation_ID
Cary Clark137b8742018-05-30 09:21:49 -04004310#Alias Generation_IDs ##
Cary Clark08895c42018-02-01 09:37:32 -05004311#Line # value reflecting contents change ##
Cary Clark73fa9722017-08-29 17:36:51 -04004312Generation_ID provides a quick way to check if Verb_Array, Point_Array, or
4313Conic_Weight has changed. Generation_ID is not a hash; identical Paths will
4314not necessarily have matching Generation_IDs.
4315
4316Empty Paths have a Generation_ID of one.
4317
4318#Method uint32_t getGenerationID() const
4319
Cary Clarkab2621d2018-01-30 10:08:57 -05004320#In Generation_ID
4321#Line # returns unique ID ##
Cary Clark09d80c02018-10-31 12:14:03 -04004322#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004323
4324#Example
4325SkPath path;
4326SkDebugf("empty genID = %u\n", path.getGenerationID());
4327path.lineTo(1, 2);
4328SkDebugf("1st lineTo genID = %u\n", path.getGenerationID());
4329path.rewind();
4330SkDebugf("empty genID = %u\n", path.getGenerationID());
4331path.lineTo(1, 2);
4332SkDebugf("2nd lineTo genID = %u\n", path.getGenerationID());
4333#StdOut
4334empty genID = 1
43351st lineTo genID = 2
4336empty genID = 1
43372nd lineTo genID = 3
4338##
4339##
4340
4341#SeeAlso operator==(const SkPath& a, const SkPath& b)
4342
4343##
4344
Cary Clark78de7512018-02-07 07:27:09 -05004345#Subtopic ##
Cary Clark73fa9722017-08-29 17:36:51 -04004346
4347# ------------------------------------------------------------------------------
4348
4349#Method bool isValid() const
Cary Clark4855f782018-02-06 09:41:53 -05004350#In Property
4351#In Utility
Cary Clarkab2621d2018-01-30 10:08:57 -05004352#Line # returns if data is internally consistent ##
Cary Clark09d80c02018-10-31 12:14:03 -04004353#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004354
Cary Clark09d80c02018-10-31 12:14:03 -04004355#NoExample
Cary Clark73fa9722017-08-29 17:36:51 -04004356 ##
4357
4358##
4359
4360#Method bool pathRefIsValid() const
Cary Clark4855f782018-02-06 09:41:53 -05004361#Deprecated soon
Cary Clark73fa9722017-08-29 17:36:51 -04004362##
4363
4364# ------------------------------------------------------------------------------
4365
Cary Clark8032b982017-07-28 11:04:54 -04004366#Class Iter
Cary Clark682c58d2018-05-16 07:07:07 -04004367#Line # data iterator ##
Cary Clark8032b982017-07-28 11:04:54 -04004368
Cary Clark73fa9722017-08-29 17:36:51 -04004369#Code
Cary Clark61313f32018-10-08 14:57:48 -04004370#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004371##
4372
Cary Clark137b8742018-05-30 09:21:49 -04004373Iterates through Verb_Array, and associated Point_Array and Conic_Weight.
4374Provides options to treat open Contours as closed, and to ignore
4375degenerate data.
4376
Cary Clark8032b982017-07-28 11:04:54 -04004377#Example
4378#Height 128
4379#Description
Cary Clark73fa9722017-08-29 17:36:51 -04004380Ignoring the actual Verbs and replacing them with Quads rounds the
Cary Clark8032b982017-07-28 11:04:54 -04004381path of the glyph.
4382##
Cary Clark73fa9722017-08-29 17:36:51 -04004383void draw(SkCanvas* canvas) {
4384 SkPaint paint;
4385 paint.setAntiAlias(true);
4386 paint.setTextSize(256);
4387 SkPath asterisk, path;
4388 paint.getTextPath("*", 1, 50, 192, &asterisk);
Cary Clark682c58d2018-05-16 07:07:07 -04004389 SkPath::Iter iter(asterisk, true);
Cary Clark73fa9722017-08-29 17:36:51 -04004390 SkPoint start[4], pts[4];
4391 iter.next(start); // skip moveTo
4392 iter.next(start); // first quadTo
4393 path.moveTo((start[0] + start[1]) * 0.5f);
4394 while (SkPath::kClose_Verb != iter.next(pts)) {
4395 path.quadTo(pts[0], (pts[0] + pts[1]) * 0.5f);
4396 }
4397 path.quadTo(start[0], (start[0] + start[1]) * 0.5f);
4398 canvas->drawPath(path, paint);
Cary Clark8032b982017-07-28 11:04:54 -04004399}
4400##
4401
4402#SeeAlso RawIter
4403
4404#Method Iter()
Cary Clark682c58d2018-05-16 07:07:07 -04004405#Line # constructs Path iterator ##
Cary Clark09d80c02018-10-31 12:14:03 -04004406#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004407
4408#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004409void draw(SkCanvas* canvas) {
4410 SkPath::Iter iter;
4411 SkPoint points[4];
4412 SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not ");
4413 SkPath path;
4414 iter.setPath(path, false);
4415 SkDebugf("iter is " "%s" "done\n", SkPath::kDone_Verb == iter.next(points) ? "" : "not ");
Cary Clark8032b982017-07-28 11:04:54 -04004416}
Cary Clark73fa9722017-08-29 17:36:51 -04004417#StdOut
4418iter is done
4419iter is done
4420##
Cary Clark8032b982017-07-28 11:04:54 -04004421##
4422
4423#SeeAlso setPath
4424
4425##
4426
4427#Method Iter(const SkPath& path, bool forceClose)
Cary Clarkd2ca79c2018-08-10 13:09:13 -04004428#Line # constructs Path iterator ##
Cary Clark09d80c02018-10-31 12:14:03 -04004429#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004430
4431#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004432void draw(SkCanvas* canvas) {
4433 auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void {
4434 SkDebugf("%s:\n", prefix);
4435 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
4436 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
4437 SkPath::Verb verb;
4438 do {
4439 SkPoint points[4];
4440 verb = iter.next(points);
4441 SkDebugf("k%s_Verb ", verbStr[(int) verb]);
4442 for (int i = 0; i < pointCount[(int) verb]; ++i) {
4443 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
4444 }
4445 if (SkPath::kConic_Verb == verb) {
4446 SkDebugf("weight = %g", iter.conicWeight());
4447 }
4448 SkDebugf("\n");
4449 } while (SkPath::kDone_Verb != verb);
4450 SkDebugf("\n");
4451 };
4452
4453 SkPath path;
4454 path.quadTo(10, 20, 30, 40);
4455 SkPath::Iter openIter(path, false);
4456 debugster("open", openIter);
4457 SkPath::Iter closedIter(path, true);
4458 debugster("closed", closedIter);
Cary Clark8032b982017-07-28 11:04:54 -04004459}
4460#StdOut
4461open:
Cary Clark682c58d2018-05-16 07:07:07 -04004462kMove_Verb {0, 0},
4463kQuad_Verb {0, 0}, {10, 20}, {30, 40},
4464kDone_Verb
Cary Clark73fa9722017-08-29 17:36:51 -04004465
4466closed:
Cary Clark682c58d2018-05-16 07:07:07 -04004467kMove_Verb {0, 0},
4468kQuad_Verb {0, 0}, {10, 20}, {30, 40},
4469kLine_Verb {30, 40}, {0, 0},
4470kClose_Verb {0, 0},
4471kDone_Verb
Cary Clark8032b982017-07-28 11:04:54 -04004472##
4473##
4474
4475#SeeAlso setPath
4476
4477##
4478
4479#Method void setPath(const SkPath& path, bool forceClose)
Cary Clarkd2ca79c2018-08-10 13:09:13 -04004480#Line # resets Iter to Path ##
Cary Clark09d80c02018-10-31 12:14:03 -04004481#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004482
4483#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004484void draw(SkCanvas* canvas) {
4485 auto debugster = [](const char* prefix, SkPath::Iter& iter) -> void {
4486 SkDebugf("%s:\n", prefix);
4487 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
4488 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
4489 SkPath::Verb verb;
4490 do {
4491 SkPoint points[4];
4492 verb = iter.next(points);
4493 SkDebugf("k%s_Verb ", verbStr[(int) verb]);
4494 for (int i = 0; i < pointCount[(int) verb]; ++i) {
4495 SkDebugf("{%g, %g}, ", points[i].fX, points[i].fY);
4496 }
4497 if (SkPath::kConic_Verb == verb) {
4498 SkDebugf("weight = %g", iter.conicWeight());
4499 }
4500 SkDebugf("\n");
4501 } while (SkPath::kDone_Verb != verb);
4502 SkDebugf("\n");
4503 };
4504
4505 SkPath path;
4506 path.quadTo(10, 20, 30, 40);
4507 SkPath::Iter iter(path, false);
4508 debugster("quad open", iter);
4509 SkPath path2;
4510 path2.conicTo(1, 2, 3, 4, .5f);
4511 iter.setPath(path2, true);
4512 debugster("conic closed", iter);
Cary Clark8032b982017-07-28 11:04:54 -04004513}
4514#StdOut
Cary Clark73fa9722017-08-29 17:36:51 -04004515quad open:
Cary Clark682c58d2018-05-16 07:07:07 -04004516kMove_Verb {0, 0},
4517kQuad_Verb {0, 0}, {10, 20}, {30, 40},
4518kDone_Verb
Cary Clark73fa9722017-08-29 17:36:51 -04004519
4520conic closed:
Cary Clark682c58d2018-05-16 07:07:07 -04004521kMove_Verb {0, 0},
Cary Clark73fa9722017-08-29 17:36:51 -04004522kConic_Verb {0, 0}, {1, 2}, {3, 4}, weight = 0.5
Cary Clark682c58d2018-05-16 07:07:07 -04004523kLine_Verb {3, 4}, {0, 0},
4524kClose_Verb {0, 0},
4525kDone_Verb
Cary Clark8032b982017-07-28 11:04:54 -04004526##
4527##
4528
4529#SeeAlso Iter(const SkPath& path, bool forceClose)
4530
4531##
4532
Cary Clark682c58d2018-05-16 07:07:07 -04004533#Method Verb next(SkPoint pts[4], bool doConsumeDegenerates = true, bool exact = false)
Cary Clarkd2ca79c2018-08-10 13:09:13 -04004534#Line # returns next Verb ##
Cary Clark09d80c02018-10-31 12:14:03 -04004535#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004536
4537#Example
Cary Clark682c58d2018-05-16 07:07:07 -04004538#Description
Cary Clark8032b982017-07-28 11:04:54 -04004539skip degenerate skips the first in a kMove_Verb pair, the kMove_Verb
4540followed by the kClose_Verb, the zero length Line and the very small Line.
4541
4542skip degenerate if exact skips the same as skip degenerate, but shows
4543the very small Line.
4544
4545skip none shows all of the Verbs and Points in Path.
4546##
Cary Clark73fa9722017-08-29 17:36:51 -04004547void draw(SkCanvas* canvas) {
4548 auto debugster = [](const char* prefix, const SkPath& path, bool degen, bool exact) -> void {
4549 SkPath::Iter iter(path, false);
4550 SkDebugf("%s:\n", prefix);
4551 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
4552 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
4553 SkPath::Verb verb;
4554 do {
4555 SkPoint points[4];
4556 verb = iter.next(points, degen, exact);
4557 SkDebugf("k%s_Verb ", verbStr[(int) verb]);
4558 for (int i = 0; i < pointCount[(int) verb]; ++i) {
4559 SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY);
4560 }
4561 SkDebugf("\n");
4562 } while (SkPath::kDone_Verb != verb);
4563 SkDebugf("\n");
4564 };
4565
4566 SkPath path;
4567 path.moveTo(10, 10);
4568 path.moveTo(20, 20);
4569 path.quadTo(10, 20, 30, 40);
4570 path.moveTo(1, 1);
4571 path.close();
4572 path.moveTo(30, 30);
4573 path.lineTo(30, 30);
4574 path.moveTo(30, 30);
4575 path.lineTo(30.00001f, 30);
4576 debugster("skip degenerate", path, true, false);
4577 debugster("skip degenerate if exact", path, true, true);
4578 debugster("skip none", path, false, false);
Cary Clark8032b982017-07-28 11:04:54 -04004579}
4580#StdOut
Cary Clark73fa9722017-08-29 17:36:51 -04004581skip degenerate:
Cary Clark682c58d2018-05-16 07:07:07 -04004582kMove_Verb {20, 20},
4583kQuad_Verb {20, 20}, {10, 20}, {30, 40},
4584kDone_Verb
Cary Clark73fa9722017-08-29 17:36:51 -04004585
4586skip degenerate if exact:
Cary Clark682c58d2018-05-16 07:07:07 -04004587kMove_Verb {20, 20},
4588kQuad_Verb {20, 20}, {10, 20}, {30, 40},
4589kMove_Verb {30, 30},
4590kLine_Verb {30, 30}, {30.00001, 30},
4591kDone_Verb
Cary Clark73fa9722017-08-29 17:36:51 -04004592
4593skip none:
Cary Clark682c58d2018-05-16 07:07:07 -04004594kMove_Verb {10, 10},
4595kMove_Verb {20, 20},
4596kQuad_Verb {20, 20}, {10, 20}, {30, 40},
4597kMove_Verb {1, 1},
4598kClose_Verb {1, 1},
4599kMove_Verb {30, 30},
4600kLine_Verb {30, 30}, {30, 30},
4601kMove_Verb {30, 30},
4602kLine_Verb {30, 30}, {30.00001, 30},
4603kDone_Verb
Cary Clark8032b982017-07-28 11:04:54 -04004604##
4605##
4606
Cary Clark682c58d2018-05-16 07:07:07 -04004607#SeeAlso Verb IsLineDegenerate IsCubicDegenerate IsQuadDegenerate
Cary Clark8032b982017-07-28 11:04:54 -04004608
4609##
4610
4611#Method SkScalar conicWeight() const
Cary Clarkd2ca79c2018-08-10 13:09:13 -04004612#Line # returns Conic_Weight ##
Cary Clark09d80c02018-10-31 12:14:03 -04004613#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004614
Cary Clark09d80c02018-10-31 12:14:03 -04004615#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004616 void draw(SkCanvas* canvas) {
4617 SkPath path;
4618 path.conicTo(1, 2, 3, 4, .5f);
Cary Clark682c58d2018-05-16 07:07:07 -04004619 SkPath::Iter iter(path, false);
Cary Clark73fa9722017-08-29 17:36:51 -04004620 SkPoint p[4];
4621 SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not ");
4622 SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not ");
4623 SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY,
4624 p[2].fX, p[2].fY);
4625 SkDebugf("conic weight: %g\n", iter.conicWeight());
Cary Clark8032b982017-07-28 11:04:54 -04004626 }
4627 #StdOut
Cary Clark73fa9722017-08-29 17:36:51 -04004628first verb is move
4629next verb is conic
4630conic points: {0,0}, {1,2}, {3,4}
Cary Clark8032b982017-07-28 11:04:54 -04004631conic weight: 0.5
4632 ##
4633 ##
4634
4635 #SeeAlso Conic_Weight
4636
4637##
4638
4639#Method bool isCloseLine() const
Cary Clark682c58d2018-05-16 07:07:07 -04004640#Line # returns if Line was generated by kClose_Verb ##
Cary Clark09d80c02018-10-31 12:14:03 -04004641#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004642
Cary Clark09d80c02018-10-31 12:14:03 -04004643#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004644void draw(SkCanvas* canvas) {
4645 SkPath path;
4646 path.moveTo(6, 7);
4647 path.conicTo(1, 2, 3, 4, .5f);
4648 path.close();
Cary Clark682c58d2018-05-16 07:07:07 -04004649 SkPath::Iter iter(path, false);
Cary Clark73fa9722017-08-29 17:36:51 -04004650 SkPoint p[4];
4651 SkDebugf("1st verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not ");
4652 SkDebugf("moveTo point: {%g,%g}\n", p[0].fX, p[0].fY);
4653 SkDebugf("2nd verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not ");
4654 SkDebugf("3rd verb is " "%s" "line\n", SkPath::kLine_Verb == iter.next(p) ? "" : "not ");
4655 SkDebugf("line points: {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY);
4656 SkDebugf("line " "%s" "generated by close\n", iter.isCloseLine() ? "" : "not ");
4657 SkDebugf("4th verb is " "%s" "close\n", SkPath::kClose_Verb == iter.next(p) ? "" : "not ");
Cary Clark8032b982017-07-28 11:04:54 -04004658}
4659 #StdOut
Cary Clark73fa9722017-08-29 17:36:51 -040046601st verb is move
4661moveTo point: {6,7}
46622nd verb is conic
46633rd verb is line
4664line points: {3,4}, {6,7}
4665line generated by close
Cary Clark8032b982017-07-28 11:04:54 -040046664th verb is close
4667 ##
4668 ##
4669
4670 #SeeAlso close()
4671##
4672
4673#Method bool isClosedContour() const
Cary Clark682c58d2018-05-16 07:07:07 -04004674#Line # returns if Contour has kClose_Verb ##
Cary Clark09d80c02018-10-31 12:14:03 -04004675#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004676
4677#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004678void draw(SkCanvas* canvas) {
4679 for (bool forceClose : { false, true } ) {
4680 SkPath path;
4681 path.conicTo(1, 2, 3, 4, .5f);
Cary Clark682c58d2018-05-16 07:07:07 -04004682 SkPath::Iter iter(path, forceClose);
Cary Clark73fa9722017-08-29 17:36:51 -04004683 SkDebugf("without close(), forceClose is %s: isClosedContour returns %s\n",
4684 forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false");
4685 path.close();
4686 iter.setPath(path, forceClose);
4687 SkDebugf("with close(), forceClose is %s: isClosedContour returns %s\n",
4688 forceClose ? "true " : "false", iter.isClosedContour() ? "true" : "false");
4689 }
Cary Clark8032b982017-07-28 11:04:54 -04004690}
4691#StdOut
Cary Clark73fa9722017-08-29 17:36:51 -04004692without close(), forceClose is false: isClosedContour returns false
4693with close(), forceClose is false: isClosedContour returns true
4694without close(), forceClose is true : isClosedContour returns true
Cary Clark8032b982017-07-28 11:04:54 -04004695with close(), forceClose is true : isClosedContour returns true
4696##
4697##
4698
4699#SeeAlso Iter(const SkPath& path, bool forceClose)
4700
4701##
Cary Clark73fa9722017-08-29 17:36:51 -04004702
4703#Class Iter ##
4704
Cary Clark8032b982017-07-28 11:04:54 -04004705#Class RawIter
Cary Clark682c58d2018-05-16 07:07:07 -04004706#Line # raw data iterator ##
Cary Clark8032b982017-07-28 11:04:54 -04004707
Cary Clark73fa9722017-08-29 17:36:51 -04004708#Code
Cary Clark61313f32018-10-08 14:57:48 -04004709#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004710##
4711
Cary Clark137b8742018-05-30 09:21:49 -04004712Iterates through Verb_Array, and associated Point_Array and Conic_Weight.
4713Verb_Array, Point_Array, and Conic_Weight are returned unaltered.
4714
Cary Clark61313f32018-10-08 14:57:48 -04004715
Cary Clark8032b982017-07-28 11:04:54 -04004716 #Method RawIter()
Cary Clark682c58d2018-05-16 07:07:07 -04004717 #Line # constructs empty Path iterator ##
Cary Clark09d80c02018-10-31 12:14:03 -04004718#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004719
Cary Clark09d80c02018-10-31 12:14:03 -04004720#NoExample
Cary Clark73fa9722017-08-29 17:36:51 -04004721 ##
4722 ##
Cary Clark8032b982017-07-28 11:04:54 -04004723
4724 #Method RawIter(const SkPath& path)
Cary Clark682c58d2018-05-16 07:07:07 -04004725 #Line # constructs with Path to iterate over ##
Cary Clark09d80c02018-10-31 12:14:03 -04004726#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004727
Cary Clark09d80c02018-10-31 12:14:03 -04004728#NoExample
Cary Clark73fa9722017-08-29 17:36:51 -04004729 ##
4730 ##
Cary Clark8032b982017-07-28 11:04:54 -04004731
4732 #Method void setPath(const SkPath& path)
Cary Clark682c58d2018-05-16 07:07:07 -04004733 #Line # sets Path to iterate over ##
Cary Clark09d80c02018-10-31 12:14:03 -04004734#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004735
Cary Clark09d80c02018-10-31 12:14:03 -04004736#NoExample
Cary Clark73fa9722017-08-29 17:36:51 -04004737 ##
4738 ##
Cary Clark8032b982017-07-28 11:04:54 -04004739
4740 #Method Verb next(SkPoint pts[4])
Cary Clark682c58d2018-05-16 07:07:07 -04004741 #Line # returns next Verb and associated Points ##
Cary Clark09d80c02018-10-31 12:14:03 -04004742#Populate
Cary Clark8032b982017-07-28 11:04:54 -04004743
Cary Clark09d80c02018-10-31 12:14:03 -04004744#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004745 void draw(SkCanvas* canvas) {
4746 SkPath path;
4747 path.moveTo(50, 60);
4748 path.quadTo(10, 20, 30, 40);
4749 path.close();
4750 path.lineTo(30, 30);
4751 path.conicTo(1, 2, 3, 4, .5f);
4752 path.cubicTo(-1, -2, -3, -4, -5, -6);
4753 SkPath::RawIter iter(path);
4754 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
4755 const int pointCount[] = { 1 , 2 , 3 , 3 , 4 , 1 , 0 };
4756 SkPath::Verb verb;
4757 do {
4758 SkPoint points[4];
4759 verb = iter.next(points);
4760 SkDebugf("k%s_Verb ", verbStr[(int) verb]);
4761 for (int i = 0; i < pointCount[(int) verb]; ++i) {
4762 SkDebugf("{%1.8g, %1.8g}, ", points[i].fX, points[i].fY);
4763 }
4764 if (SkPath::kConic_Verb == verb) {
4765 SkDebugf("weight = %g", iter.conicWeight());
4766 }
4767 SkDebugf("\n");
4768 } while (SkPath::kDone_Verb != verb);
Cary Clark8032b982017-07-28 11:04:54 -04004769 }
4770 #StdOut
Cary Clark682c58d2018-05-16 07:07:07 -04004771 kMove_Verb {50, 60},
4772 kQuad_Verb {50, 60}, {10, 20}, {30, 40},
4773 kClose_Verb {50, 60},
4774 kMove_Verb {50, 60},
4775 kLine_Verb {50, 60}, {30, 30},
Cary Clark73fa9722017-08-29 17:36:51 -04004776 kConic_Verb {30, 30}, {1, 2}, {3, 4}, weight = 0.5
Cary Clark682c58d2018-05-16 07:07:07 -04004777 kCubic_Verb {3, 4}, {-1, -2}, {-3, -4}, {-5, -6},
4778 kDone_Verb
Cary Clark8032b982017-07-28 11:04:54 -04004779 ##
4780 ##
4781
4782 #SeeAlso peek()
4783
Cary Clark73fa9722017-08-29 17:36:51 -04004784 ##
Cary Clark8032b982017-07-28 11:04:54 -04004785
4786 #Method Verb peek() const
Cary Clark682c58d2018-05-16 07:07:07 -04004787 #Line # returns next Verb ##
Cary Clark09d80c02018-10-31 12:14:03 -04004788 #Populate
Cary Clark8032b982017-07-28 11:04:54 -04004789
4790 #Example
Cary Clark73fa9722017-08-29 17:36:51 -04004791 SkPath path;
4792 path.quadTo(10, 20, 30, 40);
4793 path.conicTo(1, 2, 3, 4, .5f);
4794 path.cubicTo(1, 2, 3, 4, .5, 6);
4795 SkPath::RawIter iter(path);
4796 SkPath::Verb verb, peek = iter.peek();
4797 const char* verbStr[] = { "Move", "Line", "Quad", "Conic", "Cubic", "Close", "Done" };
4798 do {
4799 SkPoint points[4];
4800 verb = iter.next(points);
4801 SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]);
4802 peek = iter.peek();
Cary Clark8032b982017-07-28 11:04:54 -04004803 } while (SkPath::kDone_Verb != verb);
Cary Clark73fa9722017-08-29 17:36:51 -04004804 SkDebugf("peek %s %c= verb %s\n", verbStr[peek], peek == verb ? '=' : '!', verbStr[verb]);
4805 #StdOut
4806 #Volatile
4807 peek Move == verb Move
4808 peek Quad == verb Quad
4809 peek Conic == verb Conic
4810 peek Cubic == verb Cubic
4811 peek Done == verb Done
4812 peek Done == verb Done
4813 ##
Cary Clark8032b982017-07-28 11:04:54 -04004814 ##
4815
4816 #Bug 6832
Cary Clark09d80c02018-10-31 12:14:03 -04004817 # StdOut is not really volatile, it just produces the wrong result.
4818 # A simple fix changes the output of hairlines and needs to be
4819 # investigated to see if the change is correct or not.
4820 # see change 21340 (abandoned for now)
Cary Clark8032b982017-07-28 11:04:54 -04004821
Cary Clarkd2ca79c2018-08-10 13:09:13 -04004822 #SeeAlso next
Cary Clark8032b982017-07-28 11:04:54 -04004823
Cary Clark73fa9722017-08-29 17:36:51 -04004824 ##
Cary Clark8032b982017-07-28 11:04:54 -04004825
Cary Clark73fa9722017-08-29 17:36:51 -04004826 #Method SkScalar conicWeight() const
Cary Clark682c58d2018-05-16 07:07:07 -04004827 #Line # returns Conic_Weight ##
Cary Clark09d80c02018-10-31 12:14:03 -04004828#Populate
Cary Clark73fa9722017-08-29 17:36:51 -04004829
Cary Clark09d80c02018-10-31 12:14:03 -04004830#Example
Cary Clark73fa9722017-08-29 17:36:51 -04004831 void draw(SkCanvas* canvas) {
4832 SkPath path;
4833 path.conicTo(1, 2, 3, 4, .5f);
Cary Clark682c58d2018-05-16 07:07:07 -04004834 SkPath::RawIter iter(path);
Cary Clark73fa9722017-08-29 17:36:51 -04004835 SkPoint p[4];
4836 SkDebugf("first verb is " "%s" "move\n", SkPath::kMove_Verb == iter.next(p) ? "" : "not ");
4837 SkDebugf("next verb is " "%s" "conic\n", SkPath::kConic_Verb == iter.next(p) ? "" : "not ");
4838 SkDebugf("conic points: {%g,%g}, {%g,%g}, {%g,%g}\n", p[0].fX, p[0].fY, p[1].fX, p[1].fY,
4839 p[2].fX, p[2].fY);
4840 SkDebugf("conic weight: %g\n", iter.conicWeight());
Cary Clark8032b982017-07-28 11:04:54 -04004841 }
4842 #StdOut
Cary Clark73fa9722017-08-29 17:36:51 -04004843 first verb is move
4844 next verb is conic
4845 conic points: {0,0}, {1,2}, {3,4}
Cary Clark8032b982017-07-28 11:04:54 -04004846 conic weight: 0.5
4847 ##
4848 ##
4849
4850 #SeeAlso Conic_Weight
Cary Clark73fa9722017-08-29 17:36:51 -04004851
4852 ##
4853
4854#Class RawIter ##
4855
4856#Class SkPath ##
4857
4858#Topic Path ##