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