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