blob: fc16c7d08e9112d1210d229e91b9c83f8171f161 [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"
Mike Klein79aea6a2018-06-11 10:45:26 -040017#include <new>
bsalomon47cc7692016-04-26 12:56:00 -070018
19/**
20 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
21 * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
22 * reflects the styling information (e.g. is stroked). It is also possible to apply just the
23 * path effect from the style. In this case the resulting shape will include any remaining
24 * stroking information that is to be applied after the path effect.
25 *
26 * Shapes can produce keys that represent only the geometry information, not the style. Note that
27 * when styling information is applied to produce a new shape then the style has been converted
28 * to geometric information and is included in the new shape's key. When the same style is applied
29 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
30 * will be the same.
31 *
bsalomonee295642016-06-06 14:01:25 -070032 * Currently this can only be constructed from a path, rect, or rrect though it can become a path
33 * 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 -040034 * that have fast paths in the GPU backend.
bsalomon47cc7692016-04-26 12:56:00 -070035 */
36class GrShape {
37public:
bsalomon67fa4e32016-09-21 08:26:57 -070038 // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
39 // to have to worry about this. This value is exposed for unit tests.
40 static constexpr int kMaxKeyFromDataVerbCnt = 10;
41
bsalomon728b0f72016-06-27 10:00:19 -070042 GrShape() { this->initType(Type::kEmpty); }
bsalomon5e410b42016-04-28 09:30:46 -070043
bsalomon728b0f72016-06-27 10:00:19 -070044 explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
bsalomon72dc51c2016-04-27 06:46:23 -070045
bsalomon728b0f72016-06-27 10:00:19 -070046 explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
bsalomon47cc7692016-04-26 12:56:00 -070047
bsalomon728b0f72016-06-27 10:00:19 -070048 explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
bsalomon47cc7692016-04-26 12:56:00 -070049
Brian Salomonda6d0722018-01-03 13:54:35 -050050 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
bsalomon728b0f72016-06-27 10:00:19 -070051 this->initType(Type::kPath, &path);
bsalomon1b28c1a2016-06-20 12:28:17 -070052 this->attemptToSimplifyPath();
bsalomon72dc51c2016-04-27 06:46:23 -070053 }
54
bsalomon47cc7692016-04-26 12:56:00 -070055 GrShape(const SkRRect& rrect, const GrStyle& style)
bsalomon728b0f72016-06-27 10:00:19 -070056 : fStyle(style) {
57 this->initType(Type::kRRect);
58 fRRectData.fRRect = rrect;
59 fRRectData.fInverted = false;
60 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
61 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -070062 this->attemptToSimplifyRRect();
bsalomonee295642016-06-06 14:01:25 -070063 }
64
bsalomon70493962016-06-10 08:05:14 -070065 GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
66 const GrStyle& style)
bsalomon728b0f72016-06-27 10:00:19 -070067 : fStyle(style) {
68 this->initType(Type::kRRect);
69 fRRectData.fRRect = rrect;
70 fRRectData.fInverted = inverted;
bsalomonee295642016-06-06 14:01:25 -070071 if (style.pathEffect()) {
bsalomon728b0f72016-06-27 10:00:19 -070072 fRRectData.fDir = dir;
73 fRRectData.fStart = start;
74 if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
75 fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
76 } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
77 fRRectData.fStart &= 0b110;
bsalomon70493962016-06-10 08:05:14 -070078 }
bsalomonee295642016-06-06 14:01:25 -070079 } else {
bsalomon728b0f72016-06-27 10:00:19 -070080 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
bsalomonee295642016-06-06 14:01:25 -070081 }
bsalomon1b28c1a2016-06-20 12:28:17 -070082 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -070083 }
bsalomon47cc7692016-04-26 12:56:00 -070084
85 GrShape(const SkRect& rect, const GrStyle& style)
bsalomon728b0f72016-06-27 10:00:19 -070086 : fStyle(style) {
87 this->initType(Type::kRRect);
88 fRRectData.fRRect = SkRRect::MakeRect(rect);
89 fRRectData.fInverted = false;
90 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
91 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -070092 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -070093 }
bsalomon47cc7692016-04-26 12:56:00 -070094
Brian Salomonda6d0722018-01-03 13:54:35 -050095 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
bsalomon728b0f72016-06-27 10:00:19 -070096 this->initType(Type::kPath, &path);
bsalomon1b28c1a2016-06-20 12:28:17 -070097 this->attemptToSimplifyPath();
bsalomon72dc51c2016-04-27 06:46:23 -070098 }
99
bsalomon47cc7692016-04-26 12:56:00 -0700100 GrShape(const SkRRect& rrect, const SkPaint& paint)
bsalomon728b0f72016-06-27 10:00:19 -0700101 : fStyle(paint) {
102 this->initType(Type::kRRect);
103 fRRectData.fRRect = rrect;
104 fRRectData.fInverted = false;
105 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
106 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -0700107 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -0700108 }
bsalomon47cc7692016-04-26 12:56:00 -0700109
110 GrShape(const SkRect& rect, const SkPaint& paint)
bsalomon728b0f72016-06-27 10:00:19 -0700111 : fStyle(paint) {
112 this->initType(Type::kRRect);
113 fRRectData.fRRect = SkRRect::MakeRect(rect);
114 fRRectData.fInverted = false;
115 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
116 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -0700117 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -0700118 }
bsalomon47cc7692016-04-26 12:56:00 -0700119
Brian Salomone4949402018-04-26 15:22:04 -0400120 static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
121 SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
122
bsalomon47cc7692016-04-26 12:56:00 -0700123 GrShape(const GrShape&);
124 GrShape& operator=(const GrShape& that);
125
bsalomon728b0f72016-06-27 10:00:19 -0700126 ~GrShape() { this->changeType(Type::kEmpty); }
bsalomon47cc7692016-04-26 12:56:00 -0700127
Brian Salomon4f40caf2017-09-01 09:00:45 -0400128 /**
129 * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
130 * version of the shape.
131 */
132 enum class FillInversion {
133 kPreserve,
134 kFlip,
135 kForceNoninverted,
136 kForceInverted
137 };
138 /**
139 * Makes a filled shape from the pre-styled original shape and optionally modifies whether
140 * the fill is inverted or not. It's important to note that the original shape's geometry
141 * may already have been modified if doing so was neutral with respect to its style
142 * (e.g. filled paths are always closed when stored in a shape and dashed paths are always
143 * made non-inverted since dashing ignores inverseness).
144 */
145 static GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve);
146
bsalomon47cc7692016-04-26 12:56:00 -0700147 const GrStyle& style() const { return fStyle; }
148
bsalomon97fd2d42016-05-09 13:02:01 -0700149 /**
150 * Returns a shape that has either applied the path effect or path effect and stroking
151 * information from this shape's style to its geometry. Scale is used when approximating the
152 * output geometry and typically is computed from the view matrix
153 */
bsalomon425c27f2016-06-23 13:18:45 -0700154 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
bsalomon97fd2d42016-05-09 13:02:01 -0700155 return GrShape(*this, apply, scale);
156 }
bsalomon47cc7692016-04-26 12:56:00 -0700157
bsalomon7c73a532016-05-11 15:15:56 -0700158 /** Returns the unstyled geometry as a rrect if possible. */
bsalomon70493962016-06-10 08:05:14 -0700159 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
bsalomon47cc7692016-04-26 12:56:00 -0700160 if (Type::kRRect != fType) {
161 return false;
162 }
163 if (rrect) {
bsalomon728b0f72016-06-27 10:00:19 -0700164 *rrect = fRRectData.fRRect;
bsalomon47cc7692016-04-26 12:56:00 -0700165 }
bsalomonee295642016-06-06 14:01:25 -0700166 if (dir) {
bsalomon728b0f72016-06-27 10:00:19 -0700167 *dir = fRRectData.fDir;
bsalomonee295642016-06-06 14:01:25 -0700168 }
169 if (start) {
bsalomon728b0f72016-06-27 10:00:19 -0700170 *start = fRRectData.fStart;
bsalomonee295642016-06-06 14:01:25 -0700171 }
bsalomon70493962016-06-10 08:05:14 -0700172 if (inverted) {
bsalomon728b0f72016-06-27 10:00:19 -0700173 *inverted = fRRectData.fInverted;
bsalomon70493962016-06-10 08:05:14 -0700174 }
bsalomon47cc7692016-04-26 12:56:00 -0700175 return true;
176 }
177
bsalomon398e3f42016-06-13 10:22:48 -0700178 /**
179 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
180 * An inverse filled line path is still considered a line.
181 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700182 bool asLine(SkPoint pts[2], bool* inverted) const {
183 if (fType != Type::kLine) {
184 return false;
185 }
186 if (pts) {
187 pts[0] = fLineData.fPts[0];
188 pts[1] = fLineData.fPts[1];
189 }
190 if (inverted) {
191 *inverted = fLineData.fInverted;
192 }
193 return true;
194 }
bsalomon398e3f42016-06-13 10:22:48 -0700195
bsalomon7c73a532016-05-11 15:15:56 -0700196 /** Returns the unstyled geometry as a path. */
bsalomon47cc7692016-04-26 12:56:00 -0700197 void asPath(SkPath* out) const {
198 switch (fType) {
bsalomon06077562016-05-04 13:50:29 -0700199 case Type::kEmpty:
200 out->reset();
201 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400202 case Type::kInvertedEmpty:
203 out->reset();
204 out->setFillType(kDefaultPathInverseFillType);
205 break;
bsalomon47cc7692016-04-26 12:56:00 -0700206 case Type::kRRect:
207 out->reset();
bsalomon728b0f72016-06-27 10:00:19 -0700208 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
bsalomon93f66bc2016-06-21 08:35:49 -0700209 // Below matches the fill type that attemptToSimplifyPath uses.
bsalomon728b0f72016-06-27 10:00:19 -0700210 if (fRRectData.fInverted) {
bsalomona4817af2016-06-23 11:48:26 -0700211 out->setFillType(kDefaultPathInverseFillType);
bsalomon93f66bc2016-06-21 08:35:49 -0700212 } else {
bsalomona4817af2016-06-23 11:48:26 -0700213 out->setFillType(kDefaultPathFillType);
bsalomon70493962016-06-10 08:05:14 -0700214 }
bsalomon47cc7692016-04-26 12:56:00 -0700215 break;
Brian Salomone4949402018-04-26 15:22:04 -0400216 case Type::kArc:
217 SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
218 fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
219 fStyle.isSimpleFill());
220 if (fArcData.fInverted) {
221 out->setFillType(kDefaultPathInverseFillType);
222 } else {
223 out->setFillType(kDefaultPathFillType);
224 }
225 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700226 case Type::kLine:
227 out->reset();
228 out->moveTo(fLineData.fPts[0]);
229 out->lineTo(fLineData.fPts[1]);
230 if (fLineData.fInverted) {
231 out->setFillType(kDefaultPathInverseFillType);
232 } else {
233 out->setFillType(kDefaultPathFillType);
234 }
235 break;
bsalomon47cc7692016-04-26 12:56:00 -0700236 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700237 *out = this->path();
bsalomon47cc7692016-04-26 12:56:00 -0700238 break;
bsalomon47cc7692016-04-26 12:56:00 -0700239 }
240 }
241
242 /**
bsalomon7c73a532016-05-11 15:15:56 -0700243 * Returns whether the geometry is empty. Note that applying the style could produce a
Brian Salomon085c0862017-08-31 15:44:51 -0400244 * non-empty shape. It also may have an inverse fill.
bsalomon7c73a532016-05-11 15:15:56 -0700245 */
Brian Salomon085c0862017-08-31 15:44:51 -0400246 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
bsalomon7c73a532016-05-11 15:15:56 -0700247
bsalomon70493962016-06-10 08:05:14 -0700248 /**
249 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
250 * the inverse fill nature of the geometry.
251 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700252 SkRect bounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700253
bsalomon70493962016-06-10 08:05:14 -0700254 /**
255 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
256 * status).
257 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700258 SkRect styledBounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700259
bsalomon7c73a532016-05-11 15:15:56 -0700260 /**
bsalomon425c27f2016-06-23 13:18:45 -0700261 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
262 * convex path is considered to be closed if they styling reflects a fill and not otherwise.
263 * This is because filling closes all contours in the path.
264 */
265 bool knownToBeConvex() const {
266 switch (fType) {
267 case Type::kEmpty:
268 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400269 case Type::kInvertedEmpty:
270 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700271 case Type::kRRect:
272 return true;
Brian Salomone4949402018-04-26 15:22:04 -0400273 case Type::kArc:
274 return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
275 SkToBool(fArcData.fUseCenter),
276 fStyle.isSimpleFill());
bsalomon0a0f67e2016-06-28 11:56:42 -0700277 case Type::kLine:
278 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700279 case Type::kPath:
280 // SkPath.isConvex() really means "is this path convex were it to be closed" and
281 // thus doesn't give the correct answer for stroked paths, hence we also check
282 // whether the path is either filled or closed. Convex paths may only have one
283 // contour hence isLastContourClosed() is a sufficient for a convex path.
bsalomon728b0f72016-06-27 10:00:19 -0700284 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
285 this->path().isConvex();
bsalomon425c27f2016-06-23 13:18:45 -0700286 }
287 return false;
288 }
289
290 /** Is the pre-styled geometry inverse filled? */
291 bool inverseFilled() const {
292 bool ret = false;
293 switch (fType) {
294 case Type::kEmpty:
295 ret = false;
296 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400297 case Type::kInvertedEmpty:
298 ret = true;
299 break;
bsalomon425c27f2016-06-23 13:18:45 -0700300 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700301 ret = fRRectData.fInverted;
bsalomon425c27f2016-06-23 13:18:45 -0700302 break;
Brian Salomone4949402018-04-26 15:22:04 -0400303 case Type::kArc:
304 ret = fArcData.fInverted;
305 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700306 case Type::kLine:
307 ret = fLineData.fInverted;
308 break;
bsalomon425c27f2016-06-23 13:18:45 -0700309 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700310 ret = this->path().isInverseFillType();
bsalomon425c27f2016-06-23 13:18:45 -0700311 break;
312 }
313 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
314 SkASSERT(!(ret && this->style().isDashed()));
315 return ret;
316 }
317
318 /**
319 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
320 * because an arbitrary path effect could produce an inverse filled path. In other cases this
321 * can be thought of as "inverseFilledAfterStyling()".
322 */
323 bool mayBeInverseFilledAfterStyling() const {
324 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
325 // filled.
326 if (this->style().hasNonDashPathEffect()) {
327 return true;
328 }
329 return this->inverseFilled();
330 }
331
332 /**
bsalomon7c73a532016-05-11 15:15:56 -0700333 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
334 * not have any caps if stroked (modulo the effect of any path effect).
bsalomon06077562016-05-04 13:50:29 -0700335 */
336 bool knownToBeClosed() const {
337 switch (fType) {
338 case Type::kEmpty:
339 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400340 case Type::kInvertedEmpty:
341 return true;
bsalomon06077562016-05-04 13:50:29 -0700342 case Type::kRRect:
343 return true;
Brian Salomone4949402018-04-26 15:22:04 -0400344 case Type::kArc:
345 return fArcData.fUseCenter;
bsalomon0a0f67e2016-06-28 11:56:42 -0700346 case Type::kLine:
347 return false;
bsalomon06077562016-05-04 13:50:29 -0700348 case Type::kPath:
bsalomon425c27f2016-06-23 13:18:45 -0700349 // SkPath doesn't keep track of the closed status of each contour.
bsalomon728b0f72016-06-27 10:00:19 -0700350 return SkPathPriv::IsClosedSingleContour(this->path());
bsalomon06077562016-05-04 13:50:29 -0700351 }
352 return false;
353 }
354
bsalomon06115ee2016-06-07 06:28:51 -0700355 uint32_t segmentMask() const {
356 switch (fType) {
357 case Type::kEmpty:
358 return 0;
Brian Salomon085c0862017-08-31 15:44:51 -0400359 case Type::kInvertedEmpty:
360 return 0;
bsalomon06115ee2016-06-07 06:28:51 -0700361 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700362 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700363 return SkPath::kConic_SegmentMask;
Brian Salomon2fad74a2017-12-20 13:28:55 -0500364 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
365 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700366 return SkPath::kLine_SegmentMask;
367 }
368 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
Brian Salomone4949402018-04-26 15:22:04 -0400369 case Type::kArc:
370 if (fArcData.fUseCenter) {
371 return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
372 }
373 return SkPath::kConic_SegmentMask;
bsalomon0a0f67e2016-06-28 11:56:42 -0700374 case Type::kLine:
375 return SkPath::kLine_SegmentMask;
bsalomon06115ee2016-06-07 06:28:51 -0700376 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700377 return this->path().getSegmentMasks();
bsalomon06115ee2016-06-07 06:28:51 -0700378 }
379 return 0;
380 }
381
bsalomon06077562016-05-04 13:50:29 -0700382 /**
bsalomon47cc7692016-04-26 12:56:00 -0700383 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
384 * A negative value is returned if the shape has no key (shouldn't be cached).
385 */
386 int unstyledKeySize() const;
387
bsalomon425c27f2016-06-23 13:18:45 -0700388 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
389
bsalomon47cc7692016-04-26 12:56:00 -0700390 /**
391 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
392 * space allocated for the key and that unstyledKeySize() does not return a negative value
393 * for this shape.
394 */
395 void writeUnstyledKey(uint32_t* key) const;
396
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400397 /**
398 * Adds a listener to the *original* path. Typically used to invalidate cached resources when
399 * a path is no longer in-use. If the shape started out as something other than a path, this
Chris Daltonafa11582018-06-08 12:00:44 -0600400 * does nothing.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400401 */
Chris Daltonafa11582018-06-08 12:00:44 -0600402 void addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener>) const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400403
404 /**
Brian Osmanb379dcd2017-10-04 15:44:05 -0400405 * 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 -0500406 * the generation ID of the *original* path. This is the path that will receive
407 * GenIDChangeListeners added to this shape.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400408 */
409 uint32_t testingOnly_getOriginalGenerationID() const;
Brian Osmanb379dcd2017-10-04 15:44:05 -0400410 bool testingOnly_isPath() const;
Brian Salomonda6d0722018-01-03 13:54:35 -0500411 bool testingOnly_isNonVolatilePath() const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400412
bsalomon47cc7692016-04-26 12:56:00 -0700413private:
bsalomon72dc51c2016-04-27 06:46:23 -0700414 enum class Type {
415 kEmpty,
Brian Salomon085c0862017-08-31 15:44:51 -0400416 kInvertedEmpty,
bsalomon72dc51c2016-04-27 06:46:23 -0700417 kRRect,
Brian Salomone4949402018-04-26 15:22:04 -0400418 kArc,
bsalomon0a0f67e2016-06-28 11:56:42 -0700419 kLine,
bsalomon72dc51c2016-04-27 06:46:23 -0700420 kPath,
421 };
422
bsalomon728b0f72016-06-27 10:00:19 -0700423 void initType(Type type, const SkPath* path = nullptr) {
424 fType = Type::kEmpty;
425 this->changeType(type, path);
426 }
427
428 void changeType(Type type, const SkPath* path = nullptr) {
429 bool wasPath = Type::kPath == fType;
430 fType = type;
431 bool isPath = Type::kPath == type;
432 SkASSERT(!path || isPath);
433 if (wasPath && !isPath) {
434 fPathData.fPath.~SkPath();
435 } else if (!wasPath && isPath) {
436 if (path) {
437 new (&fPathData.fPath) SkPath(*path);
438 } else {
439 new (&fPathData.fPath) SkPath();
440 }
441 } else if (isPath && path) {
442 fPathData.fPath = *path;
443 }
444 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
445 fPathData.fGenID = 0;
446 }
447
448 SkPath& path() {
449 SkASSERT(Type::kPath == fType);
450 return fPathData.fPath;
451 }
452
453 const SkPath& path() const {
454 SkASSERT(Type::kPath == fType);
455 return fPathData.fPath;
456 }
457
bsalomon97fd2d42016-05-09 13:02:01 -0700458 /** Constructor used by the applyStyle() function */
459 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700460
461 /**
462 * Determines the key we should inherit from the input shape's geometry and style when
463 * we are applying the style to create a new shape.
464 */
bsalomon97fd2d42016-05-09 13:02:01 -0700465 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700466
bsalomon1b28c1a2016-06-20 12:28:17 -0700467 void attemptToSimplifyPath();
bsalomon1b28c1a2016-06-20 12:28:17 -0700468 void attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700469 void attemptToSimplifyLine();
Brian Salomone4949402018-04-26 15:22:04 -0400470 void attemptToSimplifyArc();
bsalomonee295642016-06-06 14:01:25 -0700471
Brian Salomon72f78c32017-12-21 11:56:42 -0500472 bool attemptToSimplifyStrokedLineToRRect();
473
Brian Salomonda6d0722018-01-03 13:54:35 -0500474 /** Gets the path that gen id listeners should be added to. */
475 const SkPath* originalPathForListeners() const;
476
bsalomona4817af2016-06-23 11:48:26 -0700477 // Defaults to use when there is no distinction between even/odd and winding fills.
478 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
479 static constexpr SkPath::FillType kDefaultPathInverseFillType =
480 SkPath::kInverseEvenOdd_FillType;
481
bsalomonee295642016-06-06 14:01:25 -0700482 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
483 static constexpr unsigned kDefaultRRectStart = 0;
484
485 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
486 SkPath::Direction* dir) {
487 *dir = kDefaultRRectDir;
488 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
489 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
490 if (!hasPathEffect) {
491 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
492 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700493 }
bsalomonee295642016-06-06 14:01:25 -0700494 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
495 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
496 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
497 bool swapX = rect.fLeft > rect.fRight;
498 bool swapY = rect.fTop > rect.fBottom;
499 if (swapX && swapY) {
500 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
501 return 2 * 2;
502 } else if (swapX) {
503 *dir = SkPath::kCCW_Direction;
504 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
505 return 2 * 1;
506 } else if (swapY) {
507 *dir = SkPath::kCCW_Direction;
508 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
509 return 2 * 3;
bsalomon72dc51c2016-04-27 06:46:23 -0700510 }
bsalomonee295642016-06-06 14:01:25 -0700511 return 0;
512 }
513
514 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
515 SkPath::Direction* dir) {
516 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
517 // clockwise beginning at starting index 6.
518 static constexpr unsigned kPathRRectStartIdx = 6;
519 *dir = kDefaultRRectDir;
520 if (!hasPathEffect) {
521 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
522 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700523 }
bsalomonee295642016-06-06 14:01:25 -0700524 return kPathRRectStartIdx;
bsalomon72dc51c2016-04-27 06:46:23 -0700525 }
526
bsalomon728b0f72016-06-27 10:00:19 -0700527 union {
528 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400529 SkRRect fRRect;
530 SkPath::Direction fDir;
531 unsigned fStart;
532 bool fInverted;
bsalomon728b0f72016-06-27 10:00:19 -0700533 } fRRectData;
534 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400535 SkRect fOval;
536 SkScalar fStartAngleDegrees;
537 SkScalar fSweepAngleDegrees;
538 int16_t fUseCenter;
539 int16_t fInverted;
540 } fArcData;
541 struct {
542 SkPath fPath;
bsalomon728b0f72016-06-27 10:00:19 -0700543 // Gen ID of the original path (fPath may be modified)
Brian Salomone4949402018-04-26 15:22:04 -0400544 int32_t fGenID;
bsalomon728b0f72016-06-27 10:00:19 -0700545 } fPathData;
bsalomon0a0f67e2016-06-28 11:56:42 -0700546 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400547 SkPoint fPts[2];
548 bool fInverted;
bsalomon0a0f67e2016-06-28 11:56:42 -0700549 } fLineData;
bsalomon728b0f72016-06-27 10:00:19 -0700550 };
Brian Salomone4949402018-04-26 15:22:04 -0400551 GrStyle fStyle;
552 SkTLazy<SkPath> fInheritedPathForListeners;
bsalomon47cc7692016-04-26 12:56:00 -0700553 SkAutoSTArray<8, uint32_t> fInheritedKey;
Brian Salomone4949402018-04-26 15:22:04 -0400554 Type fType;
bsalomon47cc7692016-04-26 12:56:00 -0700555};
556#endif