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