blob: 032c4d5e5711e7c0012b3a89a94857e804d9783b [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 Osmanf6f7cf62017-09-25 16:49:55 -040049 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style), fOriginalPath(path) {
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 Osmanf6f7cf62017-09-25 16:49:55 -040094 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint), fOriginalPath(path) {
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
119 GrShape(const GrShape&);
120 GrShape& operator=(const GrShape& that);
121
bsalomon728b0f72016-06-27 10:00:19 -0700122 ~GrShape() { this->changeType(Type::kEmpty); }
bsalomon47cc7692016-04-26 12:56:00 -0700123
Brian Salomon4f40caf2017-09-01 09:00:45 -0400124 /**
125 * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
126 * version of the shape.
127 */
128 enum class FillInversion {
129 kPreserve,
130 kFlip,
131 kForceNoninverted,
132 kForceInverted
133 };
134 /**
135 * Makes a filled shape from the pre-styled original shape and optionally modifies whether
136 * the fill is inverted or not. It's important to note that the original shape's geometry
137 * may already have been modified if doing so was neutral with respect to its style
138 * (e.g. filled paths are always closed when stored in a shape and dashed paths are always
139 * made non-inverted since dashing ignores inverseness).
140 */
141 static GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve);
142
bsalomon47cc7692016-04-26 12:56:00 -0700143 const GrStyle& style() const { return fStyle; }
144
bsalomon97fd2d42016-05-09 13:02:01 -0700145 /**
146 * Returns a shape that has either applied the path effect or path effect and stroking
147 * information from this shape's style to its geometry. Scale is used when approximating the
148 * output geometry and typically is computed from the view matrix
149 */
bsalomon425c27f2016-06-23 13:18:45 -0700150 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
bsalomon97fd2d42016-05-09 13:02:01 -0700151 return GrShape(*this, apply, scale);
152 }
bsalomon47cc7692016-04-26 12:56:00 -0700153
bsalomon7c73a532016-05-11 15:15:56 -0700154 /** Returns the unstyled geometry as a rrect if possible. */
bsalomon70493962016-06-10 08:05:14 -0700155 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
bsalomon47cc7692016-04-26 12:56:00 -0700156 if (Type::kRRect != fType) {
157 return false;
158 }
159 if (rrect) {
bsalomon728b0f72016-06-27 10:00:19 -0700160 *rrect = fRRectData.fRRect;
bsalomon47cc7692016-04-26 12:56:00 -0700161 }
bsalomonee295642016-06-06 14:01:25 -0700162 if (dir) {
bsalomon728b0f72016-06-27 10:00:19 -0700163 *dir = fRRectData.fDir;
bsalomonee295642016-06-06 14:01:25 -0700164 }
165 if (start) {
bsalomon728b0f72016-06-27 10:00:19 -0700166 *start = fRRectData.fStart;
bsalomonee295642016-06-06 14:01:25 -0700167 }
bsalomon70493962016-06-10 08:05:14 -0700168 if (inverted) {
bsalomon728b0f72016-06-27 10:00:19 -0700169 *inverted = fRRectData.fInverted;
bsalomon70493962016-06-10 08:05:14 -0700170 }
bsalomon47cc7692016-04-26 12:56:00 -0700171 return true;
172 }
173
bsalomon398e3f42016-06-13 10:22:48 -0700174 /**
175 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
176 * An inverse filled line path is still considered a line.
177 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700178 bool asLine(SkPoint pts[2], bool* inverted) const {
179 if (fType != Type::kLine) {
180 return false;
181 }
182 if (pts) {
183 pts[0] = fLineData.fPts[0];
184 pts[1] = fLineData.fPts[1];
185 }
186 if (inverted) {
187 *inverted = fLineData.fInverted;
188 }
189 return true;
190 }
bsalomon398e3f42016-06-13 10:22:48 -0700191
bsalomon7c73a532016-05-11 15:15:56 -0700192 /** Returns the unstyled geometry as a path. */
bsalomon47cc7692016-04-26 12:56:00 -0700193 void asPath(SkPath* out) const {
194 switch (fType) {
bsalomon06077562016-05-04 13:50:29 -0700195 case Type::kEmpty:
196 out->reset();
197 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400198 case Type::kInvertedEmpty:
199 out->reset();
200 out->setFillType(kDefaultPathInverseFillType);
201 break;
bsalomon47cc7692016-04-26 12:56:00 -0700202 case Type::kRRect:
203 out->reset();
bsalomon728b0f72016-06-27 10:00:19 -0700204 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
bsalomon93f66bc2016-06-21 08:35:49 -0700205 // Below matches the fill type that attemptToSimplifyPath uses.
bsalomon728b0f72016-06-27 10:00:19 -0700206 if (fRRectData.fInverted) {
bsalomona4817af2016-06-23 11:48:26 -0700207 out->setFillType(kDefaultPathInverseFillType);
bsalomon93f66bc2016-06-21 08:35:49 -0700208 } else {
bsalomona4817af2016-06-23 11:48:26 -0700209 out->setFillType(kDefaultPathFillType);
bsalomon70493962016-06-10 08:05:14 -0700210 }
bsalomon47cc7692016-04-26 12:56:00 -0700211 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700212 case Type::kLine:
213 out->reset();
214 out->moveTo(fLineData.fPts[0]);
215 out->lineTo(fLineData.fPts[1]);
216 if (fLineData.fInverted) {
217 out->setFillType(kDefaultPathInverseFillType);
218 } else {
219 out->setFillType(kDefaultPathFillType);
220 }
221 break;
bsalomon47cc7692016-04-26 12:56:00 -0700222 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700223 *out = this->path();
bsalomon47cc7692016-04-26 12:56:00 -0700224 break;
bsalomon47cc7692016-04-26 12:56:00 -0700225 }
226 }
227
228 /**
bsalomon7c73a532016-05-11 15:15:56 -0700229 * Returns whether the geometry is empty. Note that applying the style could produce a
Brian Salomon085c0862017-08-31 15:44:51 -0400230 * non-empty shape. It also may have an inverse fill.
bsalomon7c73a532016-05-11 15:15:56 -0700231 */
Brian Salomon085c0862017-08-31 15:44:51 -0400232 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
bsalomon7c73a532016-05-11 15:15:56 -0700233
bsalomon70493962016-06-10 08:05:14 -0700234 /**
235 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
236 * the inverse fill nature of the geometry.
237 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700238 SkRect bounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700239
bsalomon70493962016-06-10 08:05:14 -0700240 /**
241 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
242 * status).
243 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700244 SkRect styledBounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700245
bsalomon7c73a532016-05-11 15:15:56 -0700246 /**
bsalomon425c27f2016-06-23 13:18:45 -0700247 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
248 * convex path is considered to be closed if they styling reflects a fill and not otherwise.
249 * This is because filling closes all contours in the path.
250 */
251 bool knownToBeConvex() const {
252 switch (fType) {
253 case Type::kEmpty:
254 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400255 case Type::kInvertedEmpty:
256 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700257 case Type::kRRect:
258 return true;
bsalomon0a0f67e2016-06-28 11:56:42 -0700259 case Type::kLine:
260 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700261 case Type::kPath:
262 // SkPath.isConvex() really means "is this path convex were it to be closed" and
263 // thus doesn't give the correct answer for stroked paths, hence we also check
264 // whether the path is either filled or closed. Convex paths may only have one
265 // contour hence isLastContourClosed() is a sufficient for a convex path.
bsalomon728b0f72016-06-27 10:00:19 -0700266 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
267 this->path().isConvex();
bsalomon425c27f2016-06-23 13:18:45 -0700268 }
269 return false;
270 }
271
272 /** Is the pre-styled geometry inverse filled? */
273 bool inverseFilled() const {
274 bool ret = false;
275 switch (fType) {
276 case Type::kEmpty:
277 ret = false;
278 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400279 case Type::kInvertedEmpty:
280 ret = true;
281 break;
bsalomon425c27f2016-06-23 13:18:45 -0700282 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700283 ret = fRRectData.fInverted;
bsalomon425c27f2016-06-23 13:18:45 -0700284 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700285 case Type::kLine:
286 ret = fLineData.fInverted;
287 break;
bsalomon425c27f2016-06-23 13:18:45 -0700288 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700289 ret = this->path().isInverseFillType();
bsalomon425c27f2016-06-23 13:18:45 -0700290 break;
291 }
292 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
293 SkASSERT(!(ret && this->style().isDashed()));
294 return ret;
295 }
296
297 /**
298 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
299 * because an arbitrary path effect could produce an inverse filled path. In other cases this
300 * can be thought of as "inverseFilledAfterStyling()".
301 */
302 bool mayBeInverseFilledAfterStyling() const {
303 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
304 // filled.
305 if (this->style().hasNonDashPathEffect()) {
306 return true;
307 }
308 return this->inverseFilled();
309 }
310
311 /**
bsalomon7c73a532016-05-11 15:15:56 -0700312 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
313 * not have any caps if stroked (modulo the effect of any path effect).
bsalomon06077562016-05-04 13:50:29 -0700314 */
315 bool knownToBeClosed() const {
316 switch (fType) {
317 case Type::kEmpty:
318 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400319 case Type::kInvertedEmpty:
320 return true;
bsalomon06077562016-05-04 13:50:29 -0700321 case Type::kRRect:
322 return true;
bsalomon0a0f67e2016-06-28 11:56:42 -0700323 case Type::kLine:
324 return false;
bsalomon06077562016-05-04 13:50:29 -0700325 case Type::kPath:
bsalomon425c27f2016-06-23 13:18:45 -0700326 // SkPath doesn't keep track of the closed status of each contour.
bsalomon728b0f72016-06-27 10:00:19 -0700327 return SkPathPriv::IsClosedSingleContour(this->path());
bsalomon06077562016-05-04 13:50:29 -0700328 }
329 return false;
330 }
331
bsalomon06115ee2016-06-07 06:28:51 -0700332 uint32_t segmentMask() const {
333 switch (fType) {
334 case Type::kEmpty:
335 return 0;
Brian Salomon085c0862017-08-31 15:44:51 -0400336 case Type::kInvertedEmpty:
337 return 0;
bsalomon06115ee2016-06-07 06:28:51 -0700338 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700339 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700340 return SkPath::kConic_SegmentMask;
bsalomon728b0f72016-06-27 10:00:19 -0700341 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700342 return SkPath::kLine_SegmentMask;
343 }
344 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
bsalomon0a0f67e2016-06-28 11:56:42 -0700345 case Type::kLine:
346 return SkPath::kLine_SegmentMask;
bsalomon06115ee2016-06-07 06:28:51 -0700347 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700348 return this->path().getSegmentMasks();
bsalomon06115ee2016-06-07 06:28:51 -0700349 }
350 return 0;
351 }
352
bsalomon06077562016-05-04 13:50:29 -0700353 /**
bsalomon47cc7692016-04-26 12:56:00 -0700354 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
355 * A negative value is returned if the shape has no key (shouldn't be cached).
356 */
357 int unstyledKeySize() const;
358
bsalomon425c27f2016-06-23 13:18:45 -0700359 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
360
bsalomon47cc7692016-04-26 12:56:00 -0700361 /**
362 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
363 * space allocated for the key and that unstyledKeySize() does not return a negative value
364 * for this shape.
365 */
366 void writeUnstyledKey(uint32_t* key) const;
367
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400368 /**
369 * Adds a listener to the *original* path. Typically used to invalidate cached resources when
370 * a path is no longer in-use. If the shape started out as something other than a path, this
371 * does nothing (but will delete the listener).
372 */
373 void addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const;
374
375 /**
Brian Osmanb379dcd2017-10-04 15:44:05 -0400376 * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
377 * the generation ID of the *original* path.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400378 */
379 uint32_t testingOnly_getOriginalGenerationID() const;
Brian Osmanb379dcd2017-10-04 15:44:05 -0400380 bool testingOnly_isPath() const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400381
bsalomon47cc7692016-04-26 12:56:00 -0700382private:
Brian Salomon4f40caf2017-09-01 09:00:45 -0400383
bsalomon72dc51c2016-04-27 06:46:23 -0700384 enum class Type {
385 kEmpty,
Brian Salomon085c0862017-08-31 15:44:51 -0400386 kInvertedEmpty,
bsalomon72dc51c2016-04-27 06:46:23 -0700387 kRRect,
bsalomon0a0f67e2016-06-28 11:56:42 -0700388 kLine,
bsalomon72dc51c2016-04-27 06:46:23 -0700389 kPath,
390 };
391
bsalomon728b0f72016-06-27 10:00:19 -0700392 void initType(Type type, const SkPath* path = nullptr) {
393 fType = Type::kEmpty;
394 this->changeType(type, path);
395 }
396
397 void changeType(Type type, const SkPath* path = nullptr) {
398 bool wasPath = Type::kPath == fType;
399 fType = type;
400 bool isPath = Type::kPath == type;
401 SkASSERT(!path || isPath);
402 if (wasPath && !isPath) {
403 fPathData.fPath.~SkPath();
404 } else if (!wasPath && isPath) {
405 if (path) {
406 new (&fPathData.fPath) SkPath(*path);
407 } else {
408 new (&fPathData.fPath) SkPath();
409 }
410 } else if (isPath && path) {
411 fPathData.fPath = *path;
412 }
413 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
414 fPathData.fGenID = 0;
415 }
416
417 SkPath& path() {
418 SkASSERT(Type::kPath == fType);
419 return fPathData.fPath;
420 }
421
422 const SkPath& path() const {
423 SkASSERT(Type::kPath == fType);
424 return fPathData.fPath;
425 }
426
bsalomon97fd2d42016-05-09 13:02:01 -0700427 /** Constructor used by the applyStyle() function */
428 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700429
430 /**
431 * Determines the key we should inherit from the input shape's geometry and style when
432 * we are applying the style to create a new shape.
433 */
bsalomon97fd2d42016-05-09 13:02:01 -0700434 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700435
bsalomon1b28c1a2016-06-20 12:28:17 -0700436 void attemptToSimplifyPath();
bsalomon1b28c1a2016-06-20 12:28:17 -0700437 void attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700438 void attemptToSimplifyLine();
bsalomonee295642016-06-06 14:01:25 -0700439
bsalomona4817af2016-06-23 11:48:26 -0700440 // Defaults to use when there is no distinction between even/odd and winding fills.
441 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
442 static constexpr SkPath::FillType kDefaultPathInverseFillType =
443 SkPath::kInverseEvenOdd_FillType;
444
bsalomonee295642016-06-06 14:01:25 -0700445 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
446 static constexpr unsigned kDefaultRRectStart = 0;
447
448 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
449 SkPath::Direction* dir) {
450 *dir = kDefaultRRectDir;
451 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
452 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
453 if (!hasPathEffect) {
454 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
455 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700456 }
bsalomonee295642016-06-06 14:01:25 -0700457 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
458 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
459 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
460 bool swapX = rect.fLeft > rect.fRight;
461 bool swapY = rect.fTop > rect.fBottom;
462 if (swapX && swapY) {
463 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
464 return 2 * 2;
465 } else if (swapX) {
466 *dir = SkPath::kCCW_Direction;
467 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
468 return 2 * 1;
469 } else if (swapY) {
470 *dir = SkPath::kCCW_Direction;
471 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
472 return 2 * 3;
bsalomon72dc51c2016-04-27 06:46:23 -0700473 }
bsalomonee295642016-06-06 14:01:25 -0700474 return 0;
475 }
476
477 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
478 SkPath::Direction* dir) {
479 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
480 // clockwise beginning at starting index 6.
481 static constexpr unsigned kPathRRectStartIdx = 6;
482 *dir = kDefaultRRectDir;
483 if (!hasPathEffect) {
484 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
485 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700486 }
bsalomonee295642016-06-06 14:01:25 -0700487 return kPathRRectStartIdx;
bsalomon72dc51c2016-04-27 06:46:23 -0700488 }
489
490 Type fType;
bsalomon728b0f72016-06-27 10:00:19 -0700491 union {
492 struct {
493 SkRRect fRRect;
494 SkPath::Direction fDir;
495 unsigned fStart;
496 bool fInverted;
497 } fRRectData;
498 struct {
499 SkPath fPath;
500 // Gen ID of the original path (fPath may be modified)
501 int32_t fGenID;
502 } fPathData;
bsalomon0a0f67e2016-06-28 11:56:42 -0700503 struct {
504 SkPoint fPts[2];
505 bool fInverted;
506 } fLineData;
bsalomon728b0f72016-06-27 10:00:19 -0700507 };
bsalomon47cc7692016-04-26 12:56:00 -0700508 GrStyle fStyle;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400509 SkPath fOriginalPath;
bsalomon47cc7692016-04-26 12:56:00 -0700510 SkAutoSTArray<8, uint32_t> fInheritedKey;
511};
512#endif