• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(); }
line()104     skvx::float4   line()  const { SkASSERT(this->isLine());  return fRect.ltrb();     }
rect()105     const Rect&    rect()  const { SkASSERT(this->isRect());  return fRect;            }
rrect()106     const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect;           }
path()107     const SkPath&  path()  const { SkASSERT(this->isPath());  return fPath;            }
108 
109     // Update the geometry stored in the Shape and update its associated type to match. This
110     // performs no simplification, so calling setRRect() with a round rect that has isRect() return
111     // true will still be considered an rrect by Shape.
112     //
113     // These reset inversion to the default for the geometric type.
setLine(SkPoint p0,SkPoint p1)114     void setLine(SkPoint p0, SkPoint p1) {
115         this->setLine(skvx::float2{p0.fX, p0.fY}, skvx::float2{p1.fX, p1.fY});
116     }
setLine(SkV2 p0,SkV2 p1)117     void setLine(SkV2 p0, SkV2 p1) {
118         this->setLine(skvx::float2{p0.x, p0.y}, skvx::float2{p1.x, p1.y});
119     }
setLine(skvx::float2 p0,skvx::float2 p1)120     void setLine(skvx::float2 p0, skvx::float2 p1) {
121         this->setType(Type::kLine);
122         fRect = Rect(p0, p1);
123         fInverted = false;
124     }
setRect(const SkRect & rect)125     void setRect(const SkRect& rect) { this->setRect(Rect(rect)); }
setRect(const Rect & rect)126     void setRect(const Rect& rect) {
127         this->setType(Type::kRect);
128         fRect = rect;
129         fInverted = false;
130     }
setRRect(const SkRRect & rrect)131     void setRRect(const SkRRect& rrect) {
132         this->setType(Type::kRRect);
133         fRRect = rrect;
134         fInverted = false;
135     }
setPath(const SkPath & path)136     void setPath(const SkPath& path) {
137         if (fType == Type::kPath) {
138             // Assign directly
139             fPath = path;
140         } else {
141             // In-place initialize
142             this->setType(Type::kPath);
143             new (&fPath) SkPath(path);
144         }
145         fInverted = path.isInverseFillType();
146     }
147 
reset()148     void reset() {
149         this->setType(Type::kEmpty);
150         fInverted = false;
151     }
152 
153     /**
154      * Gets the size of the key for the shape represented by this Shape.
155      * A negative value is returned if the shape has no key (shouldn't be cached).
156      */
157     int keySize() const;
158 
hasKey()159     bool hasKey() const { return this->keySize() >= 0; }
160 
161     /**
162      * Writes keySize() bytes into the provided pointer. Assumes that there is enough
163      * space allocated for the key and that keySize() does not return a negative value
164      * for this shape. If includeInverted is false, non-inverted state will be written
165      * into the key regardless of the Shape's state.
166      */
167     void writeKey(uint32_t* key, bool includeInverted) const;
168 
169 private:
setType(Type type)170     void setType(Type type) {
171         if (this->isPath() && type != Type::kPath) {
172             fPath.~SkPath();
173         }
174         fType = type;
175     }
176 
177     /**
178      * Key for the state data in the shape. This includes path fill type,
179      * and any tracked inversion, as well as the class of geometry.
180      * If includeInverted is false, non-inverted state will be written into
181      * the key regardless of the Shape's state.
182      */
183     uint32_t stateKey(bool includeInverted) const;
184 
185     union {
186         Rect    fRect; // p0 = top-left, p1 = bot-right if type is kLine (may be unsorted)
187         SkRRect fRRect;
188         SkPath  fPath;
189     };
190 
191     Type    fType     = Type::kEmpty;
192     bool    fInverted = false;
193 };
194 
195 } // namespace skgpu::graphite
196 
197 #endif // skgpu_graphite_geom_Shape_DEFINED
198