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 int fLastMoveIndex; // only needed until SkPath is immutable 232 bool fNeedsMoveVerb; 233 234 enum IsA { 235 kIsA_JustMoves, // we only have 0 or more moves 236 kIsA_MoreThanMoves, // we have verbs other than just move 237 kIsA_Oval, // we are 0 or more moves followed by an oval 238 kIsA_RRect, // we are 0 or more moves followed by a rrect 239 }; 240 IsA fIsA = kIsA_JustMoves; 241 int fIsAStart = -1; // tracks direction iff fIsA is not unknown 242 bool fIsACCW = false; // tracks direction iff fIsA is not unknown 243 244 // for testing 245 SkPathConvexity fOverrideConvexity = SkPathConvexity::kUnknown; 246 countVerbs()247 int countVerbs() const { return fVerbs.count(); } 248 249 // called right before we add a (non-move) verb ensureMove()250 void ensureMove() { 251 fIsA = kIsA_MoreThanMoves; 252 if (fNeedsMoveVerb) { 253 this->moveTo(fLastMovePoint); 254 } 255 } 256 257 SkPath make(sk_sp<SkPathRef>) const; 258 259 SkPathBuilder& privateReverseAddPath(const SkPath&); 260 261 // For testing privateSetConvexity(SkPathConvexity c)262 void privateSetConvexity(SkPathConvexity c) { fOverrideConvexity = c; } 263 264 friend class SkPathPriv; 265 }; 266 267 #endif 268 269