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