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.
17struct 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.
25struct 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 */
49class GrShape {
50public:
51 // The current set of types GrShape can represent directly
52 enum class Type : uint8_t {
53 kEmpty, kPoint, kRect, kRRect, kPath, kArc, kLine, kLast = kLine
54 };
55 static constexpr int kTypeCount = static_cast<int>(Type::kLast) + 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 static constexpr SkPathDirection kDefaultDir = SkPathDirection::kCW;
60 static constexpr unsigned kDefaultStart = 0;
61 // The fill rule that is used by asPath() for shapes that aren't already a path.
62 static constexpr SkPathFillType kDefaultFillType = SkPathFillType::kEvenOdd;
63
64 GrShape() {}
65 explicit GrShape(const SkPoint& point) { this->setPoint(point); }
66 explicit GrShape(const SkRect& rect) { this->setRect(rect); }
67 explicit GrShape(const SkRRect& rrect) { this->setRRect(rrect); }
68 explicit GrShape(const SkPath& path) { this->setPath(path); }
69 explicit GrShape(const GrArc& arc) { this->setArc(arc); }
70 explicit GrShape(const GrLineSegment& line){ this->setLine(line); }
71
72 explicit GrShape(const GrShape& shape) { *this = shape; }
73
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).
83 bool isEmpty() const { return this->type() == Type::kEmpty; }
84 bool isPoint() const { return this->type() == Type::kPoint; }
85 bool isRect() const { return this->type() == Type::kRect; }
86 bool isRRect() const { return this->type() == Type::kRRect; }
87 bool isPath() const { return this->type() == Type::kPath; }
88 bool isArc() const { return this->type() == Type::kArc; }
89 bool isLine() const { return this->type() == Type::kLine; }
90
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).
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.
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.
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.
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
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.
130 SkPoint& point() { SkASSERT(this->isPoint()); return fPoint; }
131 const SkPoint& point() const { SkASSERT(this->isPoint()); return fPoint; }
132
133 SkRect& rect() { SkASSERT(this->isRect()); return fRect; }
134 const SkRect& rect() const { SkASSERT(this->isRect()); return fRect; }
135
136 SkRRect& rrect() { SkASSERT(this->isRRect()); return fRRect; }
137 const SkRRect& rrect() const { SkASSERT(this->isRRect()); return fRRect; }
138
139 SkPath& path() { SkASSERT(this->isPath()); return fPath; }
140 const SkPath& path() const { SkASSERT(this->isPath()); return fPath; }
141
142 GrArc& arc() { SkASSERT(this->isArc()); return fArc; }
143 const GrArc& arc() const { SkASSERT(this->isArc()); return fArc; }
144
145 GrLineSegment& line() { SkASSERT(this->isLine()); return fLine; }
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.
154 void setPoint(const SkPoint& point) {
155 this->reset(Type::kPoint);
156 fPoint = point;
157 }
158 void setRect(const SkRect& rect) {
159 this->reset(Type::kRect);
160 fRect = rect;
161 }
162 void setRRect(const SkRRect& rrect) {
163 this->reset(Type::kRRect);
164 fRRect = rrect;
165 }
166 void setArc(const GrArc& arc) {
167 this->reset(Type::kArc);
168 fArc = arc;
169 }
170 void setLine(const GrLineSegment& line) {
171 this->reset(Type::kLine);
172 fLine = line;
173 }
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 }
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.
211 bool contains(const SkRect& rect) const;
212
213 // True if the underlying geometry represents a closed shape, without the need for an
214 // implicit close (note that if simplified earlier with 'simpleFill' = true, a shape that was
215 // not closed may become closed).
216 bool closed() const;
217
218 // True if the underlying shape is known to be convex, assuming no other styles. If 'simpleFill'
219 // is true, it is assumed the contours will be implicitly closed when drawn or used.
220 bool convex(bool simpleFill = true) const;
221
222 // The bounding box of the shape.
223 SkRect bounds() const;
224
225 // The segment masks that describe the shape, were it to be converted to an SkPath
226 uint32_t segmentMask() const;
227
228 // Convert the shape into a path that describes the same geometry.
229 void asPath(SkPath* out, bool simpleFill = true) const;
230
231private:
232
233 void setType(Type type) {
234 if (this->isPath() && type != Type::kPath) {
235 fInverted = fPath.isInverseFillType();
236 fPath.~SkPath();
237 }
238 fType = type;
239 }
240
241 void reset(Type type) {
242 this->setType(type);
243 this->setPathWindingParams(kDefaultDir, kDefaultStart);
244 this->setInverted(false);
245 }
246
247 // Paths and arcs are root shapes, another type will never simplify to them, so they do
248 // not take the geometry to simplify as an argument. Since they are root shapes, they also
249 // return whether or not they were originally closed before being simplified.
250 bool simplifyPath(unsigned flags);
251 bool simplifyArc(unsigned flags);
252
253 // The simpler type classes do take the geometry because it may represent an in-progress
254 // simplification that hasn't been set on the GrShape yet. The simpler types do not report
255 // whether or not they were closed because it's implicit in their type.
256 void simplifyLine(const SkPoint& p1, const SkPoint& p2, unsigned flags);
257 void simplifyPoint(const SkPoint& point, unsigned flags);
258
259 // RRects and rects care about winding for path effects and will set the path winding state
260 // of the shape as well.
261 void simplifyRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start, unsigned flags);
262 void simplifyRect(const SkRect& rect, SkPathDirection dir, unsigned start, unsigned flags);
263
264 union {
265 SkPoint fPoint;
266 SkRect fRect;
267 SkRRect fRRect;
268 SkPath fPath;
269 GrArc fArc;
270 GrLineSegment fLine;
271 };
272
273 Type fType = Type::kEmpty;
274 uint8_t fStart; // Restricted to rrects and simpler, so this will be < 8
275 bool fCW;
276 bool fInverted;
277};
278
279#endif
280