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