1 /* 2 * Copyright 2020 Google LLC 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 GrShape_DEFINED 9 #define GrShape_DEFINED 10 11 #include "include/core/SkPath.h" 12 #include "include/core/SkPoint.h" 13 #include "include/core/SkRRect.h" 14 #include "include/core/SkRect.h" 15 16 // Represents an arc along an oval boundary, or a closed wedge of the oval. 17 struct GrArc { 18 SkRect fOval; // The sorted, bounding box defining the oval the arc is traced along 19 SkScalar fStartAngle; // In degrees 20 SkScalar fSweepAngle; // In degrees 21 bool fUseCenter; // True if the arc includes the center point of the oval 22 }; 23 24 // Represents a line segment between two points. 25 struct GrLineSegment { 26 SkPoint fP1; 27 SkPoint fP2; 28 }; 29 30 /** 31 * GrShape is a convenience class to represent the many different specialized geometries that 32 * Ganesh can handle, including rects, round rects, lines, as well as paths. It is intended as 33 * a data-only class where any additional complex behavior is handled by an owning type (e.g. 34 * GrStyledShape). However, it does include some basic utilities that unify common functionality 35 * (such as contains()) from the underlying shape types. 36 * 37 * In order to have lossless simplification of the geometry, it also tracks winding direction, start 38 * index, and fill inversion. The direction and index are match the SkPath indexing scheme for 39 * the shape's type (e.g. rect, rrect, or oval). 40 * 41 * Regarding GrShape's empty shape: 42 * - GrShape uses empty to refer to the absence of any geometric data 43 * - SkRect::isEmpty() returns true if the rect is not sorted, even if it has area. GrShape will not 44 * simplify these shapes to an empty GrShape. Rects with actual 0 width and height will simplify 45 * to a point or line, not empty. This is to preserve geometric data for path effects and strokes. 46 * - SkRRect::isEmpty() is true when the bounds have 0 width or height, so GrShape will simplify it 47 * to a point or line, just like a rect. SkRRect does not have the concept of unsorted edges. 48 */ 49 class GrShape { 50 public: 51 // The current set of types GrShape can represent directly 52 enum class Type : uint8_t { 53 kEmpty, kPoint, kRect, kRRect, kPath, kArc, kLine 54 }; 55 inline static constexpr int kTypeCount = static_cast<int>(Type::kLine) + 1; 56 57 // The direction and start index used when a shape does not have a representable winding, 58 // or when that information was discarded during simplification (kIgnoreWinding_Flag). 59 inline static constexpr SkPathDirection kDefaultDir = SkPathDirection::kCW; 60 inline static constexpr unsigned kDefaultStart = 0; 61 // The fill rule that is used by asPath() for shapes that aren't already a path. 62 inline static constexpr SkPathFillType kDefaultFillType = SkPathFillType::kEvenOdd; 63 GrShape()64 GrShape() {} GrShape(const SkPoint & point)65 explicit GrShape(const SkPoint& point) { this->setPoint(point); } GrShape(const SkRect & rect)66 explicit GrShape(const SkRect& rect) { this->setRect(rect); } GrShape(const SkRRect & rrect)67 explicit GrShape(const SkRRect& rrect) { this->setRRect(rrect); } GrShape(const SkPath & path)68 explicit GrShape(const SkPath& path) { this->setPath(path); } GrShape(const GrArc & arc)69 explicit GrShape(const GrArc& arc) { this->setArc(arc); } GrShape(const GrLineSegment & line)70 explicit GrShape(const GrLineSegment& line){ this->setLine(line); } 71 GrShape(const GrShape & shape)72 GrShape(const GrShape& shape) { *this = shape; } 73 ~GrShape()74 ~GrShape() { this->reset(); } 75 76 // NOTE: None of the geometry types benefit from move semantics, so we don't bother 77 // defining a move assignment operator for GrShape. 78 GrShape& operator=(const GrShape& shape); 79 80 // These type queries reflect the shape type provided when assigned, it does not incorporate 81 // any potential simplification (e.g. if isRRect() is true and rrect().isRect() is true, 82 // isRect() will still be false, until simplify() is called). isEmpty()83 bool isEmpty() const { return this->type() == Type::kEmpty; } isPoint()84 bool isPoint() const { return this->type() == Type::kPoint; } isRect()85 bool isRect() const { return this->type() == Type::kRect; } isRRect()86 bool isRRect() const { return this->type() == Type::kRRect; } isPath()87 bool isPath() const { return this->type() == Type::kPath; } isArc()88 bool isArc() const { return this->type() == Type::kArc; } isLine()89 bool isLine() const { return this->type() == Type::kLine; } 90 type()91 Type type() const { return fType; } 92 93 // Report the shape type, winding direction, start index, and invertedness as a value suitable 94 // for use in a resource key. This does not include any geometry coordinates into the key value. 95 uint32_t stateKey() const; 96 97 // Whether or not the shape is meant to be the inverse of its geometry (i.e. its exterior). inverted()98 bool inverted() const { 99 return this->isPath() ? fPath.isInverseFillType() : SkToBool(fInverted); 100 } 101 102 // Returns the path direction extracted from the path during simplification, if the shape's 103 // type represents a rrect, rect, or oval. dir()104 SkPathDirection dir() const { return fCW ? SkPathDirection::kCW : SkPathDirection::kCCW; } 105 // Returns the start index extracted from the path during simplification, if the shape's 106 // type represents a rrect, rect, or oval. startIndex()107 unsigned startIndex() const { return fStart; } 108 109 // Override the direction and start parameters for the simplified contour. These are only 110 // meaningful for rects, rrects, and ovals. setPathWindingParams(SkPathDirection dir,unsigned start)111 void setPathWindingParams(SkPathDirection dir, unsigned start) { 112 SkASSERT((this->isRect() && start < 4) || (this->isRRect() && start < 8) || 113 (dir == kDefaultDir && start == kDefaultStart)); 114 fCW = dir == SkPathDirection::kCW; 115 fStart = static_cast<uint8_t>(start); 116 } 117 setInverted(bool inverted)118 void setInverted(bool inverted) { 119 if (this->isPath()) { 120 if (inverted != fPath.isInverseFillType()) { 121 fPath.toggleInverseFillType(); 122 } 123 } else { 124 fInverted = inverted; 125 } 126 } 127 128 // Access the actual geometric description of the shape. May only access the appropriate type 129 // based on what was last set. The type may change after simplify() is called. point()130 SkPoint& point() { SkASSERT(this->isPoint()); return fPoint; } point()131 const SkPoint& point() const { SkASSERT(this->isPoint()); return fPoint; } 132 rect()133 SkRect& rect() { SkASSERT(this->isRect()); return fRect; } rect()134 const SkRect& rect() const { SkASSERT(this->isRect()); return fRect; } 135 rrect()136 SkRRect& rrect() { SkASSERT(this->isRRect()); return fRRect; } rrect()137 const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; } 138 path()139 SkPath& path() { SkASSERT(this->isPath()); return fPath; } path()140 const SkPath& path() const { SkASSERT(this->isPath()); return fPath; } 141 arc()142 GrArc& arc() { SkASSERT(this->isArc()); return fArc; } arc()143 const GrArc& arc() const { SkASSERT(this->isArc()); return fArc; } 144 line()145 GrLineSegment& line() { SkASSERT(this->isLine()); return fLine; } line()146 const GrLineSegment& line() const { SkASSERT(this->isLine()); return fLine; } 147 148 // Update the geometry stored in the GrShape and update its associated type to match. This 149 // performs no simplification, so calling setRRect() with a round rect that has isRect() return 150 // true will still be considered an rrect by this shape until simplify() is called. 151 // 152 // These also reset any extracted direction, start, and inverted state from a prior simplified 153 // path, since these functions ared used to describe a new geometry. setPoint(const SkPoint & point)154 void setPoint(const SkPoint& point) { 155 this->reset(Type::kPoint); 156 fPoint = point; 157 } setRect(const SkRect & rect)158 void setRect(const SkRect& rect) { 159 this->reset(Type::kRect); 160 fRect = rect; 161 } setRRect(const SkRRect & rrect)162 void setRRect(const SkRRect& rrect) { 163 this->reset(Type::kRRect); 164 fRRect = rrect; 165 } setArc(const GrArc & arc)166 void setArc(const GrArc& arc) { 167 this->reset(Type::kArc); 168 fArc = arc; 169 } setLine(const GrLineSegment & line)170 void setLine(const GrLineSegment& line) { 171 this->reset(Type::kLine); 172 fLine = line; 173 } setPath(const SkPath & path)174 void setPath(const SkPath& path) { 175 if (this->isPath()) { 176 // Assign directly 177 fPath = path; 178 } else { 179 // In-place initialize 180 this->setType(Type::kPath); 181 new (&fPath) SkPath(path); 182 } 183 // Must also set these since we didn't call reset() like other setX functions. 184 this->setPathWindingParams(kDefaultDir, kDefaultStart); 185 fInverted = path.isInverseFillType(); 186 } reset()187 void reset() { 188 this->reset(Type::kEmpty); 189 } 190 191 // Flags that enable more aggressive, "destructive" simplifications to the geometry 192 enum SimplifyFlags : unsigned { 193 // If set, it is assumed the original shape would have been implicitly filled when drawn or 194 // clipped, so simpler shape types that are closed can still be considered. Shapes with 195 // 0 area (i.e. points and lines) can be turned into empty. 196 kSimpleFill_Flag = 0b001, 197 // If set, simplifications that would impact a directional stroke or path effect can still 198 // be taken (e.g. dir and start are not required, arcs can be converted to ovals). 199 kIgnoreWinding_Flag = 0b010, 200 // If set, the geometry will be updated to have sorted coordinates (rects, lines), modulated 201 // sweep angles (arcs). 202 kMakeCanonical_Flag = 0b100, 203 204 kAll_Flags = 0b111 205 }; 206 // Returns true if the shape was originally closed based on type (or detected type within a 207 // path), even if the final simplification results in a point, line, or empty. 208 bool simplify(unsigned flags = kAll_Flags); 209 210 // True if the given bounding box is completely inside the shape, if it's conservatively treated 211 // as a filled, closed shape. 212 bool conservativeContains(const SkRect& rect) const; 213 bool conservativeContains(const SkPoint& point) const; 214 215 // True if the underlying geometry represents a closed shape, without the need for an 216 // implicit close (note that if simplified earlier with 'simpleFill' = true, a shape that was 217 // not closed may become closed). 218 bool closed() const; 219 220 // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill' 221 // is true, it is assumed the contours will be implicitly closed when drawn or used. 222 bool convex(bool simpleFill = true) const; 223 224 // The bounding box of the shape. 225 SkRect bounds() const; 226 227 // The segment masks that describe the shape, were it to be converted to an SkPath 228 uint32_t segmentMask() const; 229 230 // Convert the shape into a path that describes the same geometry. 231 void asPath(SkPath* out, bool simpleFill = true) const; 232 233 private: 234 setType(Type type)235 void setType(Type type) { 236 if (this->isPath() && type != Type::kPath) { 237 fInverted = fPath.isInverseFillType(); 238 fPath.~SkPath(); 239 } 240 fType = type; 241 } 242 reset(Type type)243 void reset(Type type) { 244 this->setType(type); 245 this->setPathWindingParams(kDefaultDir, kDefaultStart); 246 this->setInverted(false); 247 } 248 249 // Paths and arcs are root shapes, another type will never simplify to them, so they do 250 // not take the geometry to simplify as an argument. Since they are root shapes, they also 251 // return whether or not they were originally closed before being simplified. 252 bool simplifyPath(unsigned flags); 253 bool simplifyArc(unsigned flags); 254 255 // The simpler type classes do take the geometry because it may represent an in-progress 256 // simplification that hasn't been set on the GrShape yet. The simpler types do not report 257 // whether or not they were closed because it's implicit in their type. 258 void simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags); 259 void simplifyPoint(const SkPoint& point, unsigned flags); 260 261 // RRects and rects care about winding for path effects and will set the path winding state 262 // of the shape as well. 263 void simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start, unsigned flags); 264 void simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start, unsigned flags); 265 266 union { 267 SkPoint fPoint; 268 SkRect fRect; 269 SkRRect fRRect; 270 SkPath fPath; 271 GrArc fArc; 272 GrLineSegment fLine; 273 }; 274 275 Type fType = Type::kEmpty; 276 uint8_t fStart; // Restricted to rrects and simpler, so this will be < 8 277 bool fCW; 278 bool fInverted; 279 }; 280 281 #endif 282