1 /* 2 * Copyright 2021 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 skgpu_graphite_geom_Shape_DEFINED 9 #define skgpu_graphite_geom_Shape_DEFINED 10 11 #include "include/core/SkArc.h" 12 #include "include/core/SkM44.h" 13 #include "include/core/SkPath.h" 14 #include "include/core/SkPathTypes.h" 15 #include "include/core/SkPoint.h" 16 #include "include/core/SkRRect.h" 17 #include "include/private/base/SkAssert.h" 18 #include "src/base/SkVx.h" 19 #include "src/gpu/graphite/geom/Rect.h" 20 21 #include <cstdint> 22 #include <new> 23 24 struct SkRect; 25 26 namespace skgpu::graphite { 27 28 /** 29 * Shape is effectively a std::variant over different geometric shapes, with the most complex 30 * being an SkPath. It provides a consistent way to query geometric properties, such as convexity, 31 * point containment, or iteration. 32 */ 33 class Shape { 34 public: 35 enum class Type : uint8_t { 36 kEmpty, kLine, kRect, kRRect, kArc, kPath 37 }; 38 inline static constexpr int kTypeCount = static_cast<int>(Type::kPath) + 1; 39 Shape()40 Shape() {} Shape(const Shape & shape)41 Shape(const Shape& shape) { *this = shape; } 42 Shape(Shape&&) = delete; 43 Shape(SkPoint p0,SkPoint p1)44 Shape(SkPoint p0, SkPoint p1) { this->setLine(p0, p1); } Shape(SkV2 p0,SkV2 p1)45 Shape(SkV2 p0, SkV2 p1) { this->setLine(p0, p1); } Shape(skvx::float2 p0,skvx::float2 p1)46 Shape(skvx::float2 p0, skvx::float2 p1) { this->setLine(p0, p1); } Shape(const Rect & rect)47 explicit Shape(const Rect& rect) { this->setRect(rect); } Shape(const SkRect & rect)48 explicit Shape(const SkRect& rect) { this->setRect(rect); } Shape(const SkRRect & rrect)49 explicit Shape(const SkRRect& rrect) { this->setRRect(rrect); } Shape(const SkArc & arc)50 explicit Shape(const SkArc& arc) { this->setArc(arc); } Shape(const SkPath & path)51 explicit Shape(const SkPath& path) { this->setPath(path); } 52 ~Shape()53 ~Shape() { this->reset(); } 54 55 // NOTE: None of the geometry types benefit from move semantics, so we don't bother 56 // defining a move assignment operator for Shape. 57 Shape& operator=(Shape&&) = delete; 58 Shape& operator=(const Shape&); 59 60 // Return the type of the data last stored in the Shape, which does not incorporate any possible 61 // simplifications that could be applied to it (e.g. a degenerate round rect with 0 radius 62 // corners is kRRect and not kRect). type()63 Type type() const { return fType; } 64 isEmpty()65 bool isEmpty() const { return fType == Type::kEmpty; } isLine()66 bool isLine() const { return fType == Type::kLine; } isRect()67 bool isRect() const { return fType == Type::kRect; } isRRect()68 bool isRRect() const { return fType == Type::kRRect; } isArc()69 bool isArc() const { return fType == Type::kArc; } isPath()70 bool isPath() const { return fType == Type::kPath; } 71 inverted()72 bool inverted() const { 73 SkASSERT(fType != Type::kPath || fInverted == fPath.isInverseFillType()); 74 return fInverted; 75 } 76 setInverted(bool inverted)77 void setInverted(bool inverted) { 78 if (fType == Type::kPath && inverted != fPath.isInverseFillType()) { 79 fPath.toggleInverseFillType(); 80 } 81 fInverted = inverted; 82 } 83 fillType()84 SkPathFillType fillType() const { 85 if (fType == Type::kPath) { 86 return fPath.getFillType(); // already incorporates invertedness 87 } else { 88 return fInverted ? SkPathFillType::kInverseEvenOdd : SkPathFillType::kEvenOdd; 89 } 90 } 91 92 // True if the given bounding box is completely inside the shape, if it's conservatively treated 93 // as a filled, closed shape. 94 bool conservativeContains(const Rect& rect) const; 95 bool conservativeContains(skvx::float2 point) const; 96 97 // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill' 98 // is true, it is assumed the contours will be implicitly closed when drawn or used. 99 bool convex(bool simpleFill = true) const; 100 101 // The bounding box of the shape. 102 Rect bounds() const; 103 104 // Convert the shape into a path that describes the same geometry. 105 SkPath asPath() const; 106 107 // Access the actual geometric description of the shape. May only access the appropriate type 108 // based on what was last set. p0()109 skvx::float2 p0() const { SkASSERT(this->isLine()); return fRect.topLeft(); } p1()110 skvx::float2 p1() const { SkASSERT(this->isLine()); return fRect.botRight(); } line()111 skvx::float4 line() const { SkASSERT(this->isLine()); return fRect.ltrb(); } rect()112 const Rect& rect() const { SkASSERT(this->isRect()); return fRect; } rrect()113 const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; } arc()114 const SkArc& arc() const { SkASSERT(this->isArc()); return fArc; } path()115 const SkPath& path() const { SkASSERT(this->isPath()); return fPath; } 116 117 // Update the geometry stored in the Shape and update its associated type to match. This 118 // performs no simplification, so calling setRRect() with a round rect that has isRect() return 119 // true will still be considered an rrect by Shape. 120 // 121 // These reset inversion to the default for the geometric type. setLine(SkPoint p0,SkPoint p1)122 void setLine(SkPoint p0, SkPoint p1) { 123 this->setLine(skvx::float2{p0.fX, p0.fY}, skvx::float2{p1.fX, p1.fY}); 124 } setLine(SkV2 p0,SkV2 p1)125 void setLine(SkV2 p0, SkV2 p1) { 126 this->setLine(skvx::float2{p0.x, p0.y}, skvx::float2{p1.x, p1.y}); 127 } setLine(skvx::float2 p0,skvx::float2 p1)128 void setLine(skvx::float2 p0, skvx::float2 p1) { 129 this->setType(Type::kLine); 130 fRect = Rect(p0, p1); 131 fInverted = false; 132 } setRect(const SkRect & rect)133 void setRect(const SkRect& rect) { this->setRect(Rect(rect)); } setRect(const Rect & rect)134 void setRect(const Rect& rect) { 135 this->setType(Type::kRect); 136 fRect = rect; 137 fInverted = false; 138 } setRRect(const SkRRect & rrect)139 void setRRect(const SkRRect& rrect) { 140 this->setType(Type::kRRect); 141 fRRect = rrect; 142 fInverted = false; 143 } setArc(const SkArc & arc)144 void setArc(const SkArc& arc) { 145 this->setType(Type::kArc); 146 fArc = arc; 147 fInverted = false; 148 } setPath(const SkPath & path)149 void setPath(const SkPath& path) { 150 if (fType == Type::kPath) { 151 // Assign directly 152 fPath = path; 153 } else { 154 // In-place initialize 155 this->setType(Type::kPath); 156 new (&fPath) SkPath(path); 157 } 158 fInverted = path.isInverseFillType(); 159 } 160 reset()161 void reset() { 162 this->setType(Type::kEmpty); 163 fInverted = false; 164 } 165 166 /** 167 * Gets the size of the key for the shape represented by this Shape. 168 * A negative value is returned if the shape has no key (shouldn't be cached). 169 */ 170 int keySize() const; 171 hasKey()172 bool hasKey() const { return this->keySize() >= 0; } 173 174 /** 175 * Writes keySize() bytes into the provided pointer. Assumes that there is enough 176 * space allocated for the key and that keySize() does not return a negative value 177 * for this shape. If includeInverted is false, non-inverted state will be written 178 * into the key regardless of the Shape's state. 179 */ 180 void writeKey(uint32_t* key, bool includeInverted) const; 181 182 private: setType(Type type)183 void setType(Type type) { 184 if (this->isPath() && type != Type::kPath) { 185 fPath.~SkPath(); 186 } 187 fType = type; 188 } 189 190 /** 191 * Key for the state data in the shape. This includes path fill type, 192 * and any tracked inversion, as well as the class of geometry. 193 * If includeInverted is false, non-inverted state will be written into 194 * the key regardless of the Shape's state. 195 */ 196 uint32_t stateKey(bool includeInverted) const; 197 198 union { 199 Rect fRect; // p0 = top-left, p1 = bot-right if type is kLine (may be unsorted) 200 SkRRect fRRect; 201 SkArc fArc; 202 SkPath fPath; 203 }; 204 205 Type fType = Type::kEmpty; 206 bool fInverted = false; 207 }; 208 209 } // namespace skgpu::graphite 210 211 #endif // skgpu_graphite_geom_Shape_DEFINED 212