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 20namespace 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 */ 27class Shape { 28public: 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 34 Shape() {} 35 Shape(const Shape& shape) { *this = shape; } 36 Shape(Shape&&) = delete; 37 38 Shape(SkPoint p0, SkPoint p1) { this->setLine(p0, p1); } 39 Shape(SkV2 p0, SkV2 p1) { this->setLine(p0, p1); } 40 Shape(float2 p0, float2 p1) { this->setLine(p0, p1); } 41 explicit Shape(const Rect& rect) { this->setRect(rect); } 42 explicit Shape(const SkRect& rect) { this->setRect(rect); } 43 explicit Shape(const SkRRect& rrect) { this->setRRect(rrect); } 44 explicit Shape(const SkPath& path) { this->setPath(path); } 45 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). 56 Type type() const { return fType; } 57 58 bool isEmpty() const { return fType == Type::kEmpty; } 59 bool isLine() const { return fType == Type::kLine; } 60 bool isRect() const { return fType == Type::kRect; } 61 bool isRRect() const { return fType == Type::kRRect; } 62 bool isPath() const { return fType == Type::kPath; } 63 64 bool inverted() const { 65 SkASSERT(fType != Type::kPath || fInverted == fPath.isInverseFillType()); 66 return fInverted; 67 } 68 69 void setInverted(bool inverted) { 70 if (fType == Type::kPath && inverted != fPath.isInverseFillType()) { 71 fPath.toggleInverseFillType(); 72 } 73 fInverted = inverted; 74 } 75 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. 105 float2 p0() const { SkASSERT(this->isLine()); return fRect.topLeft(); } 106 float2 p1() const { SkASSERT(this->isLine()); return fRect.botRight(); } 107 const Rect& rect() const { SkASSERT(this->isRect()); return fRect; } 108 const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; } 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. 116 void setLine(SkPoint p0, SkPoint p1) { 117 this->setLine(float2{p0.fX, p0.fY}, float2{p1.fX, p1.fY}); 118 } 119 void setLine(SkV2 p0, SkV2 p1) { 120 this->setLine(float2{p0.x, p0.y}, float2{p1.x, p1.y}); 121 } 122 void setLine(float2 p0, float2 p1) { 123 this->setType(Type::kLine); 124 fRect = Rect(p0, p1); 125 fInverted = false; 126 } 127 void setRect(const SkRect& rect) { this->setRect(Rect(rect)); } 128 void setRect(const Rect& rect) { 129 this->setType(Type::kRect); 130 fRect = rect; 131 fInverted = false; 132 } 133 void setRRect(const SkRRect& rrect) { 134 this->setType(Type::kRRect); 135 fRRect = rrect; 136 fInverted = false; 137 } 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 150 void reset() { 151 this->setType(Type::kEmpty); 152 fInverted = false; 153 } 154 155private: 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