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