blob: 36402330cef8e2389b2afdfd77e36a036e3086a4 [file] [log] [blame]
bsalomon47cc7692016-04-26 12:56:00 -07001/*
2 * Copyright 2016 Google Inc.
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 "GrStyle.h"
12#include "SkPath.h"
bsalomonee295642016-06-06 14:01:25 -070013#include "SkPathPriv.h"
bsalomon47cc7692016-04-26 12:56:00 -070014#include "SkRRect.h"
15#include "SkTemplates.h"
16#include "SkTLazy.h"
17
18/**
19 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
20 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
21 * reflects the styling information (e.g. is stroked). It is also possible to apply just the
22 * path effect from the style. In this case the resulting shape will include any remaining
23 * stroking information that is to be applied after the path effect.
24 *
25 * Shapes can produce keys that represent only the geometry information, not the style. Note that
26 * when styling information is applied to produce a new shape then the style has been converted
27 * to geometric information and is included in the new shape's key. When the same style is applied
28 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
29 * will be the same.
30 *
bsalomonee295642016-06-06 14:01:25 -070031 * Currently this can only be constructed from a path, rect, or rrect though it can become a path
32 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries
Brian Salomon4f40caf2017-09-01 09:00:45 -040033 * that have fast paths in the GPU backend.
bsalomon47cc7692016-04-26 12:56:00 -070034 */
35class GrShape {
36public:
bsalomon67fa4e32016-09-21 08:26:57 -070037 // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
38 // to have to worry about this. This value is exposed for unit tests.
39 static constexpr int kMaxKeyFromDataVerbCnt = 10;
40
bsalomon728b0f72016-06-27 10:00:19 -070041 GrShape() { this->initType(Type::kEmpty); }
bsalomon5e410b42016-04-28 09:30:46 -070042
bsalomon728b0f72016-06-27 10:00:19 -070043 explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
bsalomon72dc51c2016-04-27 06:46:23 -070044
bsalomon728b0f72016-06-27 10:00:19 -070045 explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
bsalomon47cc7692016-04-26 12:56:00 -070046
bsalomon728b0f72016-06-27 10:00:19 -070047 explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
bsalomon47cc7692016-04-26 12:56:00 -070048
Brian Salomonda6d0722018-01-03 13:54:35 -050049 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
bsalomon728b0f72016-06-27 10:00:19 -070050 this->initType(Type::kPath, &path);
bsalomon1b28c1a2016-06-20 12:28:17 -070051 this->attemptToSimplifyPath();
bsalomon72dc51c2016-04-27 06:46:23 -070052 }
53
bsalomon47cc7692016-04-26 12:56:00 -070054 GrShape(const SkRRect& rrect, const GrStyle& style)
bsalomon728b0f72016-06-27 10:00:19 -070055 : fStyle(style) {
56 this->initType(Type::kRRect);
57 fRRectData.fRRect = rrect;
58 fRRectData.fInverted = false;
59 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
60 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -070061 this->attemptToSimplifyRRect();
bsalomonee295642016-06-06 14:01:25 -070062 }
63
bsalomon70493962016-06-10 08:05:14 -070064 GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
65 const GrStyle& style)
bsalomon728b0f72016-06-27 10:00:19 -070066 : fStyle(style) {
67 this->initType(Type::kRRect);
68 fRRectData.fRRect = rrect;
69 fRRectData.fInverted = inverted;
bsalomonee295642016-06-06 14:01:25 -070070 if (style.pathEffect()) {
bsalomon728b0f72016-06-27 10:00:19 -070071 fRRectData.fDir = dir;
72 fRRectData.fStart = start;
73 if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
74 fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
75 } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
76 fRRectData.fStart &= 0b110;
bsalomon70493962016-06-10 08:05:14 -070077 }
bsalomonee295642016-06-06 14:01:25 -070078 } else {
bsalomon728b0f72016-06-27 10:00:19 -070079 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
bsalomonee295642016-06-06 14:01:25 -070080 }
bsalomon1b28c1a2016-06-20 12:28:17 -070081 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -070082 }
bsalomon47cc7692016-04-26 12:56:00 -070083
84 GrShape(const SkRect& rect, const GrStyle& style)
bsalomon728b0f72016-06-27 10:00:19 -070085 : fStyle(style) {
86 this->initType(Type::kRRect);
87 fRRectData.fRRect = SkRRect::MakeRect(rect);
88 fRRectData.fInverted = false;
89 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
90 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -070091 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -070092 }
bsalomon47cc7692016-04-26 12:56:00 -070093
Brian Salomonda6d0722018-01-03 13:54:35 -050094 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
bsalomon728b0f72016-06-27 10:00:19 -070095 this->initType(Type::kPath, &path);
bsalomon1b28c1a2016-06-20 12:28:17 -070096 this->attemptToSimplifyPath();
bsalomon72dc51c2016-04-27 06:46:23 -070097 }
98
bsalomon47cc7692016-04-26 12:56:00 -070099 GrShape(const SkRRect& rrect, const SkPaint& paint)
bsalomon728b0f72016-06-27 10:00:19 -0700100 : fStyle(paint) {
101 this->initType(Type::kRRect);
102 fRRectData.fRRect = rrect;
103 fRRectData.fInverted = false;
104 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
105 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -0700106 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -0700107 }
bsalomon47cc7692016-04-26 12:56:00 -0700108
109 GrShape(const SkRect& rect, const SkPaint& paint)
bsalomon728b0f72016-06-27 10:00:19 -0700110 : fStyle(paint) {
111 this->initType(Type::kRRect);
112 fRRectData.fRRect = SkRRect::MakeRect(rect);
113 fRRectData.fInverted = false;
114 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
115 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -0700116 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -0700117 }
bsalomon47cc7692016-04-26 12:56:00 -0700118
Brian Salomon8a98bc92018-04-25 11:21:39 -0400119 static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
120 SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
121
bsalomon47cc7692016-04-26 12:56:00 -0700122 GrShape(const GrShape&);
123 GrShape& operator=(const GrShape& that);
124
bsalomon728b0f72016-06-27 10:00:19 -0700125 ~GrShape() { this->changeType(Type::kEmpty); }
bsalomon47cc7692016-04-26 12:56:00 -0700126
Brian Salomon4f40caf2017-09-01 09:00:45 -0400127 /**
128 * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
129 * version of the shape.
130 */
131 enum class FillInversion {
132 kPreserve,
133 kFlip,
134 kForceNoninverted,
135 kForceInverted
136 };
137 /**
138 * Makes a filled shape from the pre-styled original shape and optionally modifies whether
139 * the fill is inverted or not. It's important to note that the original shape's geometry
140 * may already have been modified if doing so was neutral with respect to its style
141 * (e.g. filled paths are always closed when stored in a shape and dashed paths are always
142 * made non-inverted since dashing ignores inverseness).
143 */
144 static GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve);
145
bsalomon47cc7692016-04-26 12:56:00 -0700146 const GrStyle& style() const { return fStyle; }
147
bsalomon97fd2d42016-05-09 13:02:01 -0700148 /**
149 * Returns a shape that has either applied the path effect or path effect and stroking
150 * information from this shape's style to its geometry. Scale is used when approximating the
151 * output geometry and typically is computed from the view matrix
152 */
bsalomon425c27f2016-06-23 13:18:45 -0700153 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
bsalomon97fd2d42016-05-09 13:02:01 -0700154 return GrShape(*this, apply, scale);
155 }
bsalomon47cc7692016-04-26 12:56:00 -0700156
bsalomon7c73a532016-05-11 15:15:56 -0700157 /** Returns the unstyled geometry as a rrect if possible. */
bsalomon70493962016-06-10 08:05:14 -0700158 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
bsalomon47cc7692016-04-26 12:56:00 -0700159 if (Type::kRRect != fType) {
160 return false;
161 }
162 if (rrect) {
bsalomon728b0f72016-06-27 10:00:19 -0700163 *rrect = fRRectData.fRRect;
bsalomon47cc7692016-04-26 12:56:00 -0700164 }
bsalomonee295642016-06-06 14:01:25 -0700165 if (dir) {
bsalomon728b0f72016-06-27 10:00:19 -0700166 *dir = fRRectData.fDir;
bsalomonee295642016-06-06 14:01:25 -0700167 }
168 if (start) {
bsalomon728b0f72016-06-27 10:00:19 -0700169 *start = fRRectData.fStart;
bsalomonee295642016-06-06 14:01:25 -0700170 }
bsalomon70493962016-06-10 08:05:14 -0700171 if (inverted) {
bsalomon728b0f72016-06-27 10:00:19 -0700172 *inverted = fRRectData.fInverted;
bsalomon70493962016-06-10 08:05:14 -0700173 }
bsalomon47cc7692016-04-26 12:56:00 -0700174 return true;
175 }
176
bsalomon398e3f42016-06-13 10:22:48 -0700177 /**
178 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
179 * An inverse filled line path is still considered a line.
180 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700181 bool asLine(SkPoint pts[2], bool* inverted) const {
182 if (fType != Type::kLine) {
183 return false;
184 }
185 if (pts) {
186 pts[0] = fLineData.fPts[0];
187 pts[1] = fLineData.fPts[1];
188 }
189 if (inverted) {
190 *inverted = fLineData.fInverted;
191 }
192 return true;
193 }
bsalomon398e3f42016-06-13 10:22:48 -0700194
bsalomon7c73a532016-05-11 15:15:56 -0700195 /** Returns the unstyled geometry as a path. */
bsalomon47cc7692016-04-26 12:56:00 -0700196 void asPath(SkPath* out) const {
197 switch (fType) {
bsalomon06077562016-05-04 13:50:29 -0700198 case Type::kEmpty:
199 out->reset();
200 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400201 case Type::kInvertedEmpty:
202 out->reset();
203 out->setFillType(kDefaultPathInverseFillType);
204 break;
bsalomon47cc7692016-04-26 12:56:00 -0700205 case Type::kRRect:
206 out->reset();
bsalomon728b0f72016-06-27 10:00:19 -0700207 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
bsalomon93f66bc2016-06-21 08:35:49 -0700208 // Below matches the fill type that attemptToSimplifyPath uses.
bsalomon728b0f72016-06-27 10:00:19 -0700209 if (fRRectData.fInverted) {
bsalomona4817af2016-06-23 11:48:26 -0700210 out->setFillType(kDefaultPathInverseFillType);
bsalomon93f66bc2016-06-21 08:35:49 -0700211 } else {
bsalomona4817af2016-06-23 11:48:26 -0700212 out->setFillType(kDefaultPathFillType);
bsalomon70493962016-06-10 08:05:14 -0700213 }
bsalomon47cc7692016-04-26 12:56:00 -0700214 break;
Brian Salomon8a98bc92018-04-25 11:21:39 -0400215 case Type::kArc:
216 SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
217 fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
218 fStyle.isSimpleFill());
219 if (fArcData.fInverted) {
220 out->setFillType(kDefaultPathInverseFillType);
221 } else {
222 out->setFillType(kDefaultPathFillType);
223 }
224 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700225 case Type::kLine:
226 out->reset();
227 out->moveTo(fLineData.fPts[0]);
228 out->lineTo(fLineData.fPts[1]);
229 if (fLineData.fInverted) {
230 out->setFillType(kDefaultPathInverseFillType);
231 } else {
232 out->setFillType(kDefaultPathFillType);
233 }
234 break;
bsalomon47cc7692016-04-26 12:56:00 -0700235 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700236 *out = this->path();
bsalomon47cc7692016-04-26 12:56:00 -0700237 break;
bsalomon47cc7692016-04-26 12:56:00 -0700238 }
239 }
240
241 /**
bsalomon7c73a532016-05-11 15:15:56 -0700242 * Returns whether the geometry is empty. Note that applying the style could produce a
Brian Salomon085c0862017-08-31 15:44:51 -0400243 * non-empty shape. It also may have an inverse fill.
bsalomon7c73a532016-05-11 15:15:56 -0700244 */
Brian Salomon085c0862017-08-31 15:44:51 -0400245 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
bsalomon7c73a532016-05-11 15:15:56 -0700246
bsalomon70493962016-06-10 08:05:14 -0700247 /**
248 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
249 * the inverse fill nature of the geometry.
250 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700251 SkRect bounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700252
bsalomon70493962016-06-10 08:05:14 -0700253 /**
254 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
255 * status).
256 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700257 SkRect styledBounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700258
bsalomon7c73a532016-05-11 15:15:56 -0700259 /**
bsalomon425c27f2016-06-23 13:18:45 -0700260 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
261 * convex path is considered to be closed if they styling reflects a fill and not otherwise.
262 * This is because filling closes all contours in the path.
263 */
264 bool knownToBeConvex() const {
265 switch (fType) {
266 case Type::kEmpty:
267 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400268 case Type::kInvertedEmpty:
269 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700270 case Type::kRRect:
271 return true;
Brian Salomon8a98bc92018-04-25 11:21:39 -0400272 case Type::kArc:
273 return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
274 SkToBool(fArcData.fUseCenter),
275 fStyle.isSimpleFill());
bsalomon0a0f67e2016-06-28 11:56:42 -0700276 case Type::kLine:
277 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700278 case Type::kPath:
279 // SkPath.isConvex() really means "is this path convex were it to be closed" and
280 // thus doesn't give the correct answer for stroked paths, hence we also check
281 // whether the path is either filled or closed. Convex paths may only have one
282 // contour hence isLastContourClosed() is a sufficient for a convex path.
bsalomon728b0f72016-06-27 10:00:19 -0700283 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
284 this->path().isConvex();
bsalomon425c27f2016-06-23 13:18:45 -0700285 }
286 return false;
287 }
288
289 /** Is the pre-styled geometry inverse filled? */
290 bool inverseFilled() const {
291 bool ret = false;
292 switch (fType) {
293 case Type::kEmpty:
294 ret = false;
295 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400296 case Type::kInvertedEmpty:
297 ret = true;
298 break;
bsalomon425c27f2016-06-23 13:18:45 -0700299 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700300 ret = fRRectData.fInverted;
bsalomon425c27f2016-06-23 13:18:45 -0700301 break;
Brian Salomon8a98bc92018-04-25 11:21:39 -0400302 case Type::kArc:
303 ret = fArcData.fInverted;
304 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700305 case Type::kLine:
306 ret = fLineData.fInverted;
307 break;
bsalomon425c27f2016-06-23 13:18:45 -0700308 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700309 ret = this->path().isInverseFillType();
bsalomon425c27f2016-06-23 13:18:45 -0700310 break;
311 }
312 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
313 SkASSERT(!(ret && this->style().isDashed()));
314 return ret;
315 }
316
317 /**
318 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
319 * because an arbitrary path effect could produce an inverse filled path. In other cases this
320 * can be thought of as "inverseFilledAfterStyling()".
321 */
322 bool mayBeInverseFilledAfterStyling() const {
323 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
324 // filled.
325 if (this->style().hasNonDashPathEffect()) {
326 return true;
327 }
328 return this->inverseFilled();
329 }
330
331 /**
bsalomon7c73a532016-05-11 15:15:56 -0700332 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
333 * not have any caps if stroked (modulo the effect of any path effect).
bsalomon06077562016-05-04 13:50:29 -0700334 */
335 bool knownToBeClosed() const {
336 switch (fType) {
337 case Type::kEmpty:
338 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400339 case Type::kInvertedEmpty:
340 return true;
bsalomon06077562016-05-04 13:50:29 -0700341 case Type::kRRect:
342 return true;
Brian Salomon8a98bc92018-04-25 11:21:39 -0400343 case Type::kArc:
344 return fArcData.fUseCenter;
bsalomon0a0f67e2016-06-28 11:56:42 -0700345 case Type::kLine:
346 return false;
bsalomon06077562016-05-04 13:50:29 -0700347 case Type::kPath:
bsalomon425c27f2016-06-23 13:18:45 -0700348 // SkPath doesn't keep track of the closed status of each contour.
bsalomon728b0f72016-06-27 10:00:19 -0700349 return SkPathPriv::IsClosedSingleContour(this->path());
bsalomon06077562016-05-04 13:50:29 -0700350 }
351 return false;
352 }
353
bsalomon06115ee2016-06-07 06:28:51 -0700354 uint32_t segmentMask() const {
355 switch (fType) {
356 case Type::kEmpty:
357 return 0;
Brian Salomon085c0862017-08-31 15:44:51 -0400358 case Type::kInvertedEmpty:
359 return 0;
bsalomon06115ee2016-06-07 06:28:51 -0700360 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700361 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700362 return SkPath::kConic_SegmentMask;
Brian Salomon2fad74a2017-12-20 13:28:55 -0500363 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
364 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700365 return SkPath::kLine_SegmentMask;
366 }
367 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
Brian Salomon8a98bc92018-04-25 11:21:39 -0400368 case Type::kArc:
369 if (fArcData.fUseCenter) {
370 return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
371 }
372 return SkPath::kConic_SegmentMask;
bsalomon0a0f67e2016-06-28 11:56:42 -0700373 case Type::kLine:
374 return SkPath::kLine_SegmentMask;
bsalomon06115ee2016-06-07 06:28:51 -0700375 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700376 return this->path().getSegmentMasks();
bsalomon06115ee2016-06-07 06:28:51 -0700377 }
378 return 0;
379 }
380
bsalomon06077562016-05-04 13:50:29 -0700381 /**
bsalomon47cc7692016-04-26 12:56:00 -0700382 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
383 * A negative value is returned if the shape has no key (shouldn't be cached).
384 */
385 int unstyledKeySize() const;
386
bsalomon425c27f2016-06-23 13:18:45 -0700387 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
388
bsalomon47cc7692016-04-26 12:56:00 -0700389 /**
390 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
391 * space allocated for the key and that unstyledKeySize() does not return a negative value
392 * for this shape.
393 */
394 void writeUnstyledKey(uint32_t* key) const;
395
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400396 /**
397 * Adds a listener to the *original* path. Typically used to invalidate cached resources when
398 * a path is no longer in-use. If the shape started out as something other than a path, this
399 * does nothing (but will delete the listener).
400 */
401 void addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const;
402
403 /**
Brian Osmanb379dcd2017-10-04 15:44:05 -0400404 * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
Brian Salomonda6d0722018-01-03 13:54:35 -0500405 * the generation ID of the *original* path. This is the path that will receive
406 * GenIDChangeListeners added to this shape.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400407 */
408 uint32_t testingOnly_getOriginalGenerationID() const;
Brian Osmanb379dcd2017-10-04 15:44:05 -0400409 bool testingOnly_isPath() const;
Brian Salomonda6d0722018-01-03 13:54:35 -0500410 bool testingOnly_isNonVolatilePath() const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400411
bsalomon47cc7692016-04-26 12:56:00 -0700412private:
bsalomon72dc51c2016-04-27 06:46:23 -0700413 enum class Type {
414 kEmpty,
Brian Salomon085c0862017-08-31 15:44:51 -0400415 kInvertedEmpty,
bsalomon72dc51c2016-04-27 06:46:23 -0700416 kRRect,
Brian Salomon8a98bc92018-04-25 11:21:39 -0400417 kArc,
bsalomon0a0f67e2016-06-28 11:56:42 -0700418 kLine,
bsalomon72dc51c2016-04-27 06:46:23 -0700419 kPath,
420 };
421
bsalomon728b0f72016-06-27 10:00:19 -0700422 void initType(Type type, const SkPath* path = nullptr) {
423 fType = Type::kEmpty;
424 this->changeType(type, path);
425 }
426
427 void changeType(Type type, const SkPath* path = nullptr) {
428 bool wasPath = Type::kPath == fType;
429 fType = type;
430 bool isPath = Type::kPath == type;
431 SkASSERT(!path || isPath);
432 if (wasPath && !isPath) {
433 fPathData.fPath.~SkPath();
434 } else if (!wasPath && isPath) {
435 if (path) {
436 new (&fPathData.fPath) SkPath(*path);
437 } else {
438 new (&fPathData.fPath) SkPath();
439 }
440 } else if (isPath && path) {
441 fPathData.fPath = *path;
442 }
443 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
444 fPathData.fGenID = 0;
445 }
446
447 SkPath& path() {
448 SkASSERT(Type::kPath == fType);
449 return fPathData.fPath;
450 }
451
452 const SkPath& path() const {
453 SkASSERT(Type::kPath == fType);
454 return fPathData.fPath;
455 }
456
bsalomon97fd2d42016-05-09 13:02:01 -0700457 /** Constructor used by the applyStyle() function */
458 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700459
460 /**
461 * Determines the key we should inherit from the input shape's geometry and style when
462 * we are applying the style to create a new shape.
463 */
bsalomon97fd2d42016-05-09 13:02:01 -0700464 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700465
bsalomon1b28c1a2016-06-20 12:28:17 -0700466 void attemptToSimplifyPath();
bsalomon1b28c1a2016-06-20 12:28:17 -0700467 void attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700468 void attemptToSimplifyLine();
Brian Salomon8a98bc92018-04-25 11:21:39 -0400469 void attemptToSimplifyArc();
bsalomonee295642016-06-06 14:01:25 -0700470
Brian Salomon72f78c32017-12-21 11:56:42 -0500471 bool attemptToSimplifyStrokedLineToRRect();
472
Brian Salomonda6d0722018-01-03 13:54:35 -0500473 /** Gets the path that gen id listeners should be added to. */
474 const SkPath* originalPathForListeners() const;
475
bsalomona4817af2016-06-23 11:48:26 -0700476 // Defaults to use when there is no distinction between even/odd and winding fills.
477 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
478 static constexpr SkPath::FillType kDefaultPathInverseFillType =
479 SkPath::kInverseEvenOdd_FillType;
480
bsalomonee295642016-06-06 14:01:25 -0700481 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
482 static constexpr unsigned kDefaultRRectStart = 0;
483
484 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
485 SkPath::Direction* dir) {
486 *dir = kDefaultRRectDir;
487 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
488 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
489 if (!hasPathEffect) {
490 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
491 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700492 }
bsalomonee295642016-06-06 14:01:25 -0700493 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
494 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
495 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
496 bool swapX = rect.fLeft > rect.fRight;
497 bool swapY = rect.fTop > rect.fBottom;
498 if (swapX && swapY) {
499 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
500 return 2 * 2;
501 } else if (swapX) {
502 *dir = SkPath::kCCW_Direction;
503 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
504 return 2 * 1;
505 } else if (swapY) {
506 *dir = SkPath::kCCW_Direction;
507 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
508 return 2 * 3;
bsalomon72dc51c2016-04-27 06:46:23 -0700509 }
bsalomonee295642016-06-06 14:01:25 -0700510 return 0;
511 }
512
513 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
514 SkPath::Direction* dir) {
515 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
516 // clockwise beginning at starting index 6.
517 static constexpr unsigned kPathRRectStartIdx = 6;
518 *dir = kDefaultRRectDir;
519 if (!hasPathEffect) {
520 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
521 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700522 }
bsalomonee295642016-06-06 14:01:25 -0700523 return kPathRRectStartIdx;
bsalomon72dc51c2016-04-27 06:46:23 -0700524 }
525
bsalomon728b0f72016-06-27 10:00:19 -0700526 union {
527 struct {
Brian Salomon8a98bc92018-04-25 11:21:39 -0400528 SkRRect fRRect;
529 SkPath::Direction fDir;
530 unsigned fStart;
531 bool fInverted;
bsalomon728b0f72016-06-27 10:00:19 -0700532 } fRRectData;
533 struct {
Brian Salomon8a98bc92018-04-25 11:21:39 -0400534 SkRect fOval;
535 SkScalar fStartAngleDegrees;
536 SkScalar fSweepAngleDegrees;
537 int16_t fUseCenter;
538 int16_t fInverted;
539 } fArcData;
540 struct {
541 SkPath fPath;
bsalomon728b0f72016-06-27 10:00:19 -0700542 // Gen ID of the original path (fPath may be modified)
Brian Salomon8a98bc92018-04-25 11:21:39 -0400543 int32_t fGenID;
bsalomon728b0f72016-06-27 10:00:19 -0700544 } fPathData;
bsalomon0a0f67e2016-06-28 11:56:42 -0700545 struct {
Brian Salomon8a98bc92018-04-25 11:21:39 -0400546 SkPoint fPts[2];
547 bool fInverted;
bsalomon0a0f67e2016-06-28 11:56:42 -0700548 } fLineData;
bsalomon728b0f72016-06-27 10:00:19 -0700549 };
Brian Salomon8a98bc92018-04-25 11:21:39 -0400550 GrStyle fStyle;
551 SkTLazy<SkPath> fInheritedPathForListeners;
bsalomon47cc7692016-04-26 12:56:00 -0700552 SkAutoSTArray<8, uint32_t> fInheritedKey;
Brian Salomon8a98bc92018-04-25 11:21:39 -0400553 Type fType;
bsalomon47cc7692016-04-26 12:56:00 -0700554};
555#endif