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