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 | |
16 | class SK_API SkPathBuilder { |
17 | public: |
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 , int ); |
193 | void incReserve(int ) { |
194 | this->incReserve(extraPtCount, extraPtCount); |
195 | } |
196 | |
197 | private: |
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 | |