• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 GrShape_DEFINED
9 #define GrShape_DEFINED
10 
11 #include "include/core/SkPath.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkRRect.h"
14 #include "include/core/SkRect.h"
15 
16 // Represents an arc along an oval boundary, or a closed wedge of the oval.
17 struct GrArc {
18     SkRect   fOval;       // The sorted, bounding box defining the oval the arc is traced along
19     SkScalar fStartAngle; // In degrees
20     SkScalar fSweepAngle; // In degrees
21     bool     fUseCenter;  // True if the arc includes the center point of the oval
22 };
23 
24 // Represents a line segment between two points.
25 struct GrLineSegment {
26     SkPoint fP1;
27     SkPoint fP2;
28 };
29 
30 /**
31  * GrShape is a convenience class to represent the many different specialized geometries that
32  * Ganesh can handle, including rects, round rects, lines, as well as paths. It is intended as
33  * a data-only class where any additional complex behavior is handled by an owning type (e.g.
34  * GrStyledShape). However, it does include some basic utilities that unify common functionality
35  * (such as contains()) from the underlying shape types.
36  *
37  * In order to have lossless simplification of the geometry, it also tracks winding direction, start
38  * index, and fill inversion. The direction and index are match the SkPath indexing scheme for
39  * the shape's type (e.g. rect, rrect, or oval).
40  *
41  * Regarding GrShape's empty shape:
42  * - GrShape uses empty to refer to the absence of any geometric data
43  * - SkRect::isEmpty() returns true if the rect is not sorted, even if it has area. GrShape will not
44  *   simplify these shapes to an empty GrShape. Rects with actual 0 width and height will simplify
45  *   to a point or line, not empty. This is to preserve geometric data for path effects and strokes.
46  * - SkRRect::isEmpty() is true when the bounds have 0 width or height, so GrShape will simplify it
47  *   to a point or line, just like a rect. SkRRect does not have the concept of unsorted edges.
48  */
49 class GrShape {
50 public:
51     // The current set of types GrShape can represent directly
52     enum class Type : uint8_t {
53         kEmpty, kPoint, kRect, kRRect, kPath, kArc, kLine
54     };
55     inline static constexpr int kTypeCount = static_cast<int>(Type::kLine) + 1;
56 
57     // The direction and start index used when a shape does not have a representable winding,
58     // or when that information was discarded during simplification (kIgnoreWinding_Flag).
59     inline static constexpr SkPathDirection kDefaultDir   = SkPathDirection::kCW;
60     inline static constexpr unsigned        kDefaultStart = 0;
61     // The fill rule that is used by asPath() for shapes that aren't already a path.
62     inline static constexpr SkPathFillType  kDefaultFillType = SkPathFillType::kEvenOdd;
63 
GrShape()64     GrShape() {}
GrShape(const SkPoint & point)65     explicit GrShape(const SkPoint& point) { this->setPoint(point); }
GrShape(const SkRect & rect)66     explicit GrShape(const SkRect& rect) { this->setRect(rect); }
GrShape(const SkRRect & rrect)67     explicit GrShape(const SkRRect& rrect) { this->setRRect(rrect); }
GrShape(const SkPath & path)68     explicit GrShape(const SkPath& path) { this->setPath(path); }
GrShape(const GrArc & arc)69     explicit GrShape(const GrArc& arc) { this->setArc(arc); }
GrShape(const GrLineSegment & line)70     explicit GrShape(const GrLineSegment& line){ this->setLine(line); }
71 
GrShape(const GrShape & shape)72     GrShape(const GrShape& shape) { *this = shape; }
73 
~GrShape()74     ~GrShape() { this->reset(); }
75 
76     // NOTE: None of the geometry types benefit from move semantics, so we don't bother
77     // defining a move assignment operator for GrShape.
78     GrShape& operator=(const GrShape& shape);
79 
80     // These type queries reflect the shape type provided when assigned, it does not incorporate
81     // any potential simplification (e.g. if isRRect() is true and rrect().isRect() is true,
82     // isRect() will still be false, until simplify() is called).
isEmpty()83     bool isEmpty() const { return this->type() == Type::kEmpty; }
isPoint()84     bool isPoint() const { return this->type() == Type::kPoint; }
isRect()85     bool isRect() const { return this->type() == Type::kRect; }
isRRect()86     bool isRRect() const { return this->type() == Type::kRRect; }
isPath()87     bool isPath() const { return this->type() == Type::kPath; }
isArc()88     bool isArc() const { return this->type() == Type::kArc; }
isLine()89     bool isLine() const { return this->type() == Type::kLine; }
90 
type()91     Type type() const { return fType; }
92 
93     // Report the shape type, winding direction, start index, and invertedness as a value suitable
94     // for use in a resource key. This does not include any geometry coordinates into the key value.
95     uint32_t stateKey() const;
96 
97     // Whether or not the shape is meant to be the inverse of its geometry (i.e. its exterior).
inverted()98     bool inverted() const {
99         return this->isPath() ? fPath.isInverseFillType() : SkToBool(fInverted);
100     }
101 
102     // Returns the path direction extracted from the path during simplification, if the shape's
103     // type represents a rrect, rect, or oval.
dir()104     SkPathDirection dir() const { return fCW ? SkPathDirection::kCW : SkPathDirection::kCCW; }
105     // Returns the start index extracted from the path during simplification, if the shape's
106     // type represents a rrect, rect, or oval.
startIndex()107     unsigned startIndex() const { return fStart; }
108 
109     // Override the direction and start parameters for the simplified contour. These are only
110     // meaningful for rects, rrects, and ovals.
setPathWindingParams(SkPathDirection dir,unsigned start)111     void setPathWindingParams(SkPathDirection dir, unsigned start) {
112         SkASSERT((this->isRect() && start < 4) || (this->isRRect() && start < 8) ||
113                  (dir == kDefaultDir && start == kDefaultStart));
114         fCW = dir == SkPathDirection::kCW;
115         fStart = static_cast<uint8_t>(start);
116     }
117 
setInverted(bool inverted)118     void setInverted(bool inverted) {
119         if (this->isPath()) {
120             if (inverted != fPath.isInverseFillType()) {
121                 fPath.toggleInverseFillType();
122             }
123         } else {
124             fInverted = inverted;
125         }
126     }
127 
128     // Access the actual geometric description of the shape. May only access the appropriate type
129     // based on what was last set. The type may change after simplify() is called.
point()130     SkPoint& point() { SkASSERT(this->isPoint()); return fPoint; }
point()131     const SkPoint& point() const { SkASSERT(this->isPoint()); return fPoint; }
132 
rect()133     SkRect& rect() { SkASSERT(this->isRect()); return fRect; }
rect()134     const SkRect& rect() const { SkASSERT(this->isRect()); return fRect; }
135 
rrect()136     SkRRect& rrect() { SkASSERT(this->isRRect()); return fRRect; }
rrect()137     const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; }
138 
path()139     SkPath& path() { SkASSERT(this->isPath()); return fPath; }
path()140     const SkPath& path() const { SkASSERT(this->isPath()); return fPath; }
141 
arc()142     GrArc& arc() { SkASSERT(this->isArc()); return fArc; }
arc()143     const GrArc& arc() const { SkASSERT(this->isArc()); return fArc; }
144 
line()145     GrLineSegment& line() { SkASSERT(this->isLine()); return fLine; }
line()146     const GrLineSegment& line() const { SkASSERT(this->isLine()); return fLine; }
147 
148     // Update the geometry stored in the GrShape and update its associated type to match. This
149     // performs no simplification, so calling setRRect() with a round rect that has isRect() return
150     // true will still be considered an rrect by this shape until simplify() is called.
151     //
152     // These also reset any extracted direction, start, and inverted state from a prior simplified
153     // path, since these functions ared used to describe a new geometry.
setPoint(const SkPoint & point)154     void setPoint(const SkPoint& point) {
155         this->reset(Type::kPoint);
156         fPoint = point;
157     }
setRect(const SkRect & rect)158     void setRect(const SkRect& rect) {
159         this->reset(Type::kRect);
160         fRect = rect;
161     }
setRRect(const SkRRect & rrect)162     void setRRect(const SkRRect& rrect) {
163         this->reset(Type::kRRect);
164         fRRect = rrect;
165     }
setArc(const GrArc & arc)166     void setArc(const GrArc& arc) {
167         this->reset(Type::kArc);
168         fArc = arc;
169     }
setLine(const GrLineSegment & line)170     void setLine(const GrLineSegment& line) {
171         this->reset(Type::kLine);
172         fLine = line;
173     }
setPath(const SkPath & path)174     void setPath(const SkPath& path) {
175         if (this->isPath()) {
176             // Assign directly
177             fPath = path;
178         } else {
179             // In-place initialize
180             this->setType(Type::kPath);
181             new (&fPath) SkPath(path);
182         }
183         // Must also set these since we didn't call reset() like other setX functions.
184         this->setPathWindingParams(kDefaultDir, kDefaultStart);
185         fInverted = path.isInverseFillType();
186     }
reset()187     void reset() {
188         this->reset(Type::kEmpty);
189     }
190 
191     // Flags that enable more aggressive, "destructive" simplifications to the geometry
192     enum SimplifyFlags : unsigned {
193         // If set, it is assumed the original shape would have been implicitly filled when drawn or
194         // clipped, so simpler shape types that are closed can still be considered. Shapes with
195         // 0 area (i.e. points and lines) can be turned into empty.
196         kSimpleFill_Flag    = 0b001,
197         // If set, simplifications that would impact a directional stroke or path effect can still
198         // be taken (e.g. dir and start are not required, arcs can be converted to ovals).
199         kIgnoreWinding_Flag = 0b010,
200         // If set, the geometry will be updated to have sorted coordinates (rects, lines), modulated
201         // sweep angles (arcs).
202         kMakeCanonical_Flag = 0b100,
203 
204         kAll_Flags          = 0b111
205     };
206     // Returns true if the shape was originally closed based on type (or detected type within a
207     // path), even if the final simplification results in a point, line, or empty.
208     bool simplify(unsigned flags = kAll_Flags);
209 
210     // True if the given bounding box is completely inside the shape, if it's conservatively treated
211     // as a filled, closed shape.
212     bool conservativeContains(const SkRect& rect) const;
213     bool conservativeContains(const SkPoint& point) const;
214 
215     // True if the underlying geometry represents a closed shape, without the need for an
216     // implicit close (note that if simplified earlier with 'simpleFill' = true, a shape that was
217     // not closed may become closed).
218     bool closed() const;
219 
220     // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill'
221     // is true, it is assumed the contours will be implicitly closed when drawn or used.
222     bool convex(bool simpleFill = true) const;
223 
224     // The bounding box of the shape.
225     SkRect bounds() const;
226 
227     // The segment masks that describe the shape, were it to be converted to an SkPath
228     uint32_t segmentMask() const;
229 
230     // Convert the shape into a path that describes the same geometry.
231     void asPath(SkPath* out, bool simpleFill = true) const;
232 
233 private:
234 
setType(Type type)235     void setType(Type type) {
236         if (this->isPath() && type != Type::kPath) {
237             fInverted = fPath.isInverseFillType();
238             fPath.~SkPath();
239         }
240         fType = type;
241     }
242 
reset(Type type)243     void reset(Type type) {
244         this->setType(type);
245         this->setPathWindingParams(kDefaultDir, kDefaultStart);
246         this->setInverted(false);
247     }
248 
249     // Paths and arcs are root shapes, another type will never simplify to them, so they do
250     // not take the geometry to simplify as an argument. Since they are root shapes, they also
251     // return whether or not they were originally closed before being simplified.
252     bool simplifyPath(unsigned flags);
253     bool simplifyArc(unsigned flags);
254 
255     // The simpler type classes do take the geometry because it may represent an in-progress
256     // simplification that hasn't been set on the GrShape yet. The simpler types do not report
257     // whether or not they were closed because it's implicit in their type.
258     void simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags);
259     void simplifyPoint(const SkPoint& point, unsigned flags);
260 
261     // RRects and rects care about winding for path effects and will set the path winding state
262     // of the shape as well.
263     void simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start, unsigned flags);
264     void simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start, unsigned flags);
265 
266     union {
267         SkPoint       fPoint;
268         SkRect        fRect;
269         SkRRect       fRRect;
270         SkPath        fPath;
271         GrArc         fArc;
272         GrLineSegment fLine;
273     };
274 
275     Type            fType = Type::kEmpty;
276     uint8_t         fStart; // Restricted to rrects and simpler, so this will be < 8
277     bool            fCW;
278     bool            fInverted;
279 };
280 
281 #endif
282