1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkPathBuilder_DEFINED
9#define SkPathBuilder_DEFINED
10
11#include "include/core/SkMatrix.h"
12#include "include/core/SkPath.h"
13#include "include/core/SkPathTypes.h"
14#include "include/private/SkTDArray.h"
15
16class SK_API SkPathBuilder {
17public:
18 SkPathBuilder();
19 ~SkPathBuilder();
20
21 SkPath snapshot(); // the builder is unchanged after returning this path
22 SkPath detach(); // the builder is reset to empty after returning this path
23
24 SkPathBuilder& setFillType(SkPathFillType ft) { fFillType = ft; return *this; }
25 SkPathBuilder& setIsVolatile(bool isVolatile) { fIsVolatile = isVolatile; return *this; }
26
27 SkPathBuilder& reset();
28
29 SkPathBuilder& moveTo(SkPoint pt);
30 SkPathBuilder& moveTo(SkScalar x, SkScalar y) { return this->moveTo(SkPoint::Make(x, y)); }
31
32 SkPathBuilder& lineTo(SkPoint pt);
33 SkPathBuilder& lineTo(SkScalar x, SkScalar y) { return this->lineTo(SkPoint::Make(x, y)); }
34
35 SkPathBuilder& quadTo(SkPoint pt1, SkPoint pt2);
36 SkPathBuilder& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
37 return this->quadTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2));
38 }
39 SkPathBuilder& quadTo(const SkPoint pts[2]) { return this->quadTo(pts[0], pts[1]); }
40
41 SkPathBuilder& conicTo(SkPoint pt1, SkPoint pt2, SkScalar w);
42 SkPathBuilder& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w) {
43 return this->conicTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), w);
44 }
45 SkPathBuilder& conicTo(const SkPoint pts[2], SkScalar w) {
46 return this->conicTo(pts[0], pts[1], w);
47 }
48
49 SkPathBuilder& cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3);
50 SkPathBuilder& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) {
51 return this->cubicTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), SkPoint::Make(x3, y3));
52 }
53 SkPathBuilder& cubicTo(const SkPoint pts[3]) {
54 return this->cubicTo(pts[0], pts[1], pts[2]);
55 }
56
57 SkPathBuilder& close();
58
59 // Relative versions of segments, relative to the previous position.
60
61 SkPathBuilder& rLineTo(SkPoint pt);
62 SkPathBuilder& rLineTo(SkScalar x, SkScalar y) { return this->rLineTo({x, y}); }
63 SkPathBuilder& rQuadTo(SkPoint pt1, SkPoint pt2);
64 SkPathBuilder& rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
65 return this->rQuadTo({x1, y1}, {x2, y2});
66 }
67 SkPathBuilder& rConicTo(SkPoint p1, SkPoint p2, SkScalar w);
68 SkPathBuilder& rConicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w) {
69 return this->rConicTo({x1, y1}, {x2, y2}, w);
70 }
71 SkPathBuilder& rCubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3);
72 SkPathBuilder& rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) {
73 return this->rCubicTo({x1, y1}, {x2, y2}, {x3, y3});
74 }
75
76 // Arcs
77
78 /** Appends arc to the builder. Arc added is part of ellipse
79 bounded by oval, from startAngle through sweepAngle. Both startAngle and
80 sweepAngle are measured in degrees, where zero degrees is aligned with the
81 positive x-axis, and positive sweeps extends arc clockwise.
82
83 arcTo() adds line connecting the builder's last point to initial arc point if forceMoveTo
84 is false and the builder is not empty. Otherwise, added contour begins with first point
85 of arc. Angles greater than -360 and less than 360 are treated modulo 360.
86
87 @param oval bounds of ellipse containing arc
88 @param startAngleDeg starting angle of arc in degrees
89 @param sweepAngleDeg sweep, in degrees. Positive is clockwise; treated modulo 360
90 @param forceMoveTo true to start a new contour with arc
91 @return reference to the builder
92 */
93 SkPathBuilder& arcTo(const SkRect& oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg,
94 bool forceMoveTo);
95
96 /** Appends arc to SkPath, after appending line if needed. Arc is implemented by conic
97 weighted to describe part of circle. Arc is contained by tangent from
98 last SkPath point to p1, and tangent from p1 to p2. Arc
99 is part of circle sized to radius, positioned so it touches both tangent lines.
100
101 If last SkPath SkPoint does not start arc, arcTo() appends connecting line to SkPath.
102 The length of vector from p1 to p2 does not affect arc.
103
104 Arc sweep is always less than 180 degrees. If radius is zero, or if
105 tangents are nearly parallel, arcTo() appends line from last SkPath SkPoint to p1.
106
107 arcTo() appends at most one line and one conic.
108 arcTo() implements the functionality of PostScript arct and HTML Canvas arcTo.
109
110 @param p1 SkPoint common to pair of tangents
111 @param p2 end of second tangent
112 @param radius distance from arc to circle center
113 @return reference to SkPath
114 */
115 SkPathBuilder& arcTo(SkPoint p1, SkPoint p2, SkScalar radius);
116
117 enum ArcSize {
118 kSmall_ArcSize, //!< smaller of arc pair
119 kLarge_ArcSize, //!< larger of arc pair
120 };
121
122 /** Appends arc to SkPath. Arc is implemented by one or more conic weighted to describe
123 part of oval with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves
124 from last SkPath SkPoint to (xy.fX, xy.fY), choosing one of four possible routes:
125 clockwise or counterclockwise,
126 and smaller or larger.
127
128 Arc sweep is always less than 360 degrees. arcTo() appends line to xy if either
129 radii are zero, or if last SkPath SkPoint equals (xy.fX, xy.fY). arcTo() scales radii r to
130 fit last SkPath SkPoint and xy if both are greater than zero but too small to describe
131 an arc.
132
133 arcTo() appends up to four conic curves.
134 arcTo() implements the functionality of SVG arc, although SVG sweep-flag value is
135 opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise, while
136 kCW_Direction cast to int is zero.
137
138 @param r radii on axes before x-axis rotation
139 @param xAxisRotate x-axis rotation in degrees; positive values are clockwise
140 @param largeArc chooses smaller or larger arc
141 @param sweep chooses clockwise or counterclockwise arc
142 @param xy end of arc
143 @return reference to SkPath
144 */
145 SkPathBuilder& arcTo(SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, SkPathDirection sweep,
146 SkPoint xy);
147
148 /** Appends arc to the builder, as the start of new contour. Arc added is part of ellipse
149 bounded by oval, from startAngle through sweepAngle. Both startAngle and
150 sweepAngle are measured in degrees, where zero degrees is aligned with the
151 positive x-axis, and positive sweeps extends arc clockwise.
152
153 If sweepAngle <= -360, or sweepAngle >= 360; and startAngle modulo 90 is nearly
154 zero, append oval instead of arc. Otherwise, sweepAngle values are treated
155 modulo 360, and arc may or may not draw depending on numeric rounding.
156
157 @param oval bounds of ellipse containing arc
158 @param startAngleDeg starting angle of arc in degrees
159 @param sweepAngleDeg sweep, in degrees. Positive is clockwise; treated modulo 360
160 @return reference to this builder
161 */
162 SkPathBuilder& addArc(const SkRect& oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg);
163
164 // Add a new contour
165
166 SkPathBuilder& addRect(const SkRect&, SkPathDirection, unsigned startIndex);
167 SkPathBuilder& addOval(const SkRect&, SkPathDirection, unsigned startIndex);
168 SkPathBuilder& addRRect(const SkRRect&, SkPathDirection, unsigned startIndex);
169
170 SkPathBuilder& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) {
171 return this->addRect(rect, dir, 0);
172 }
173 SkPathBuilder& addOval(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) {
174 // legacy start index: 1
175 return this->addOval(rect, dir, 1);
176 }
177 SkPathBuilder& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW) {
178 // legacy start indices: 6 (CW) and 7 (CCW)
179 return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7);
180 }
181
182 SkPathBuilder& addCircle(SkScalar center_x, SkScalar center_y, SkScalar radius,
183 SkPathDirection dir = SkPathDirection::kCW);
184
185 SkPathBuilder& addPolygon(const SkPoint pts[], int count, bool isClosed);
186 SkPathBuilder& addPolygon(const std::initializer_list<SkPoint>& list, bool isClosed) {
187 return this->addPolygon(list.begin(), SkToInt(list.size()), isClosed);
188 }
189
190 // Performance hint, to reserve extra storage for subsequent calls to lineTo, quadTo, etc.
191
192 void incReserve(int extraPtCount, int extraVerbCount);
193 void incReserve(int extraPtCount) {
194 this->incReserve(extraPtCount, extraPtCount);
195 }
196
197private:
198 SkTDArray<SkPoint> fPts;
199 SkTDArray<uint8_t> fVerbs;
200 SkTDArray<SkScalar> fConicWeights;
201
202 SkPathFillType fFillType;
203 bool fIsVolatile;
204
205 unsigned fSegmentMask;
206 SkPoint fLastMovePoint;
207 bool fNeedsMoveVerb;
208
209 enum IsA {
210 kIsA_JustMoves, // we only have 0 or more moves
211 kIsA_MoreThanMoves, // we have verbs other than just move
212 kIsA_Oval, // we are 0 or more moves followed by an oval
213 kIsA_RRect, // we are 0 or more moves followed by a rrect
214 };
215 IsA fIsA = kIsA_JustMoves;
216 int fIsAStart = -1; // tracks direction iff fIsA is not unknown
217 bool fIsACCW = false; // tracks direction iff fIsA is not unknown
218
219 int countVerbs() const { return fVerbs.count(); }
220
221 // called right before we add a (non-move) verb
222 void ensureMove() {
223 fIsA = kIsA_MoreThanMoves;
224 if (fNeedsMoveVerb) {
225 this->moveTo(fLastMovePoint);
226 }
227 }
228
229 SkPath make(sk_sp<SkPathRef>) const;
230};
231
232#endif
233
234