• 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_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