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