• 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(); }
rect()104     const Rect&    rect()  const { SkASSERT(this->isRect());  return fRect;            }
rrect()105     const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect;           }
path()106     const SkPath&  path()  const { SkASSERT(this->isPath());  return fPath;            }
107 
108     // Update the geometry stored in the Shape and update its associated type to match. This
109     // performs no simplification, so calling setRRect() with a round rect that has isRect() return
110     // true will still be considered an rrect by Shape.
111     //
112     // These reset inversion to the default for the geometric type.
setLine(SkPoint p0,SkPoint p1)113     void setLine(SkPoint p0, SkPoint p1) {
114         this->setLine(skvx::float2{p0.fX, p0.fY}, skvx::float2{p1.fX, p1.fY});
115     }
setLine(SkV2 p0,SkV2 p1)116     void setLine(SkV2 p0, SkV2 p1) {
117         this->setLine(skvx::float2{p0.x, p0.y}, skvx::float2{p1.x, p1.y});
118     }
setLine(skvx::float2 p0,skvx::float2 p1)119     void setLine(skvx::float2 p0, skvx::float2 p1) {
120         this->setType(Type::kLine);
121         fRect = Rect(p0, p1);
122         fInverted = false;
123     }
setRect(const SkRect & rect)124     void setRect(const SkRect& rect) { this->setRect(Rect(rect)); }
setRect(const Rect & rect)125     void setRect(const Rect& rect) {
126         this->setType(Type::kRect);
127         fRect = rect;
128         fInverted = false;
129     }
setRRect(const SkRRect & rrect)130     void setRRect(const SkRRect& rrect) {
131         this->setType(Type::kRRect);
132         fRRect = rrect;
133         fInverted = false;
134     }
setPath(const SkPath & path)135     void setPath(const SkPath& path) {
136         if (fType == Type::kPath) {
137             // Assign directly
138             fPath = path;
139         } else {
140             // In-place initialize
141             this->setType(Type::kPath);
142             new (&fPath) SkPath(path);
143         }
144         fInverted = path.isInverseFillType();
145     }
146 
reset()147     void reset() {
148         this->setType(Type::kEmpty);
149         fInverted = false;
150     }
151 
152 private:
setType(Type type)153     void setType(Type type) {
154         if (this->isPath() && type != Type::kPath) {
155             fPath.~SkPath();
156         }
157         fType = type;
158     }
159 
160     union {
161         Rect    fRect; // p0 = top-left, p1 = bot-right if type is kLine (may be unsorted)
162         SkRRect fRRect;
163         SkPath  fPath;
164     };
165 
166     Type    fType     = Type::kEmpty;
167     bool    fInverted = false;
168 };
169 
170 } // namespace skgpu::graphite
171 
172 #endif // skgpu_graphite_geom_Shape_DEFINED
173