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