blob: 5226c239cda861b658c2790dc547a75fc7dfcf27 [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
33 * that have SkCanvas::draw APIs.
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
bsalomon728b0f72016-06-27 10:00:19 -070049 GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
50 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
bsalomon728b0f72016-06-27 10:00:19 -070094 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
95 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
124 const GrStyle& style() const { return fStyle; }
125
bsalomon97fd2d42016-05-09 13:02:01 -0700126 /**
127 * Returns a shape that has either applied the path effect or path effect and stroking
128 * information from this shape's style to its geometry. Scale is used when approximating the
129 * output geometry and typically is computed from the view matrix
130 */
bsalomon425c27f2016-06-23 13:18:45 -0700131 GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
bsalomon97fd2d42016-05-09 13:02:01 -0700132 return GrShape(*this, apply, scale);
133 }
bsalomon47cc7692016-04-26 12:56:00 -0700134
bsalomon7c73a532016-05-11 15:15:56 -0700135 /** Returns the unstyled geometry as a rrect if possible. */
bsalomon70493962016-06-10 08:05:14 -0700136 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
bsalomon47cc7692016-04-26 12:56:00 -0700137 if (Type::kRRect != fType) {
138 return false;
139 }
140 if (rrect) {
bsalomon728b0f72016-06-27 10:00:19 -0700141 *rrect = fRRectData.fRRect;
bsalomon47cc7692016-04-26 12:56:00 -0700142 }
bsalomonee295642016-06-06 14:01:25 -0700143 if (dir) {
bsalomon728b0f72016-06-27 10:00:19 -0700144 *dir = fRRectData.fDir;
bsalomonee295642016-06-06 14:01:25 -0700145 }
146 if (start) {
bsalomon728b0f72016-06-27 10:00:19 -0700147 *start = fRRectData.fStart;
bsalomonee295642016-06-06 14:01:25 -0700148 }
bsalomon70493962016-06-10 08:05:14 -0700149 if (inverted) {
bsalomon728b0f72016-06-27 10:00:19 -0700150 *inverted = fRRectData.fInverted;
bsalomon70493962016-06-10 08:05:14 -0700151 }
bsalomon47cc7692016-04-26 12:56:00 -0700152 return true;
153 }
154
bsalomon398e3f42016-06-13 10:22:48 -0700155 /**
156 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
157 * An inverse filled line path is still considered a line.
158 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700159 bool asLine(SkPoint pts[2], bool* inverted) const {
160 if (fType != Type::kLine) {
161 return false;
162 }
163 if (pts) {
164 pts[0] = fLineData.fPts[0];
165 pts[1] = fLineData.fPts[1];
166 }
167 if (inverted) {
168 *inverted = fLineData.fInverted;
169 }
170 return true;
171 }
bsalomon398e3f42016-06-13 10:22:48 -0700172
bsalomon7c73a532016-05-11 15:15:56 -0700173 /** Returns the unstyled geometry as a path. */
bsalomon47cc7692016-04-26 12:56:00 -0700174 void asPath(SkPath* out) const {
175 switch (fType) {
bsalomon06077562016-05-04 13:50:29 -0700176 case Type::kEmpty:
177 out->reset();
178 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400179 case Type::kInvertedEmpty:
180 out->reset();
181 out->setFillType(kDefaultPathInverseFillType);
182 break;
bsalomon47cc7692016-04-26 12:56:00 -0700183 case Type::kRRect:
184 out->reset();
bsalomon728b0f72016-06-27 10:00:19 -0700185 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
bsalomon93f66bc2016-06-21 08:35:49 -0700186 // Below matches the fill type that attemptToSimplifyPath uses.
bsalomon728b0f72016-06-27 10:00:19 -0700187 if (fRRectData.fInverted) {
bsalomona4817af2016-06-23 11:48:26 -0700188 out->setFillType(kDefaultPathInverseFillType);
bsalomon93f66bc2016-06-21 08:35:49 -0700189 } else {
bsalomona4817af2016-06-23 11:48:26 -0700190 out->setFillType(kDefaultPathFillType);
bsalomon70493962016-06-10 08:05:14 -0700191 }
bsalomon47cc7692016-04-26 12:56:00 -0700192 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700193 case Type::kLine:
194 out->reset();
195 out->moveTo(fLineData.fPts[0]);
196 out->lineTo(fLineData.fPts[1]);
197 if (fLineData.fInverted) {
198 out->setFillType(kDefaultPathInverseFillType);
199 } else {
200 out->setFillType(kDefaultPathFillType);
201 }
202 break;
bsalomon47cc7692016-04-26 12:56:00 -0700203 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700204 *out = this->path();
bsalomon47cc7692016-04-26 12:56:00 -0700205 break;
bsalomon47cc7692016-04-26 12:56:00 -0700206 }
207 }
208
209 /**
bsalomon7c73a532016-05-11 15:15:56 -0700210 * Returns whether the geometry is empty. Note that applying the style could produce a
Brian Salomon085c0862017-08-31 15:44:51 -0400211 * non-empty shape. It also may have an inverse fill.
bsalomon7c73a532016-05-11 15:15:56 -0700212 */
Brian Salomon085c0862017-08-31 15:44:51 -0400213 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
bsalomon7c73a532016-05-11 15:15:56 -0700214
bsalomon70493962016-06-10 08:05:14 -0700215 /**
216 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
217 * the inverse fill nature of the geometry.
218 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700219 SkRect bounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700220
bsalomon70493962016-06-10 08:05:14 -0700221 /**
222 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
223 * status).
224 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700225 SkRect styledBounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700226
bsalomon7c73a532016-05-11 15:15:56 -0700227 /**
bsalomon425c27f2016-06-23 13:18:45 -0700228 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
229 * convex path is considered to be closed if they styling reflects a fill and not otherwise.
230 * This is because filling closes all contours in the path.
231 */
232 bool knownToBeConvex() const {
233 switch (fType) {
234 case Type::kEmpty:
235 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400236 case Type::kInvertedEmpty:
237 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700238 case Type::kRRect:
239 return true;
bsalomon0a0f67e2016-06-28 11:56:42 -0700240 case Type::kLine:
241 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700242 case Type::kPath:
243 // SkPath.isConvex() really means "is this path convex were it to be closed" and
244 // thus doesn't give the correct answer for stroked paths, hence we also check
245 // whether the path is either filled or closed. Convex paths may only have one
246 // contour hence isLastContourClosed() is a sufficient for a convex path.
bsalomon728b0f72016-06-27 10:00:19 -0700247 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
248 this->path().isConvex();
bsalomon425c27f2016-06-23 13:18:45 -0700249 }
250 return false;
251 }
252
253 /** Is the pre-styled geometry inverse filled? */
254 bool inverseFilled() const {
255 bool ret = false;
256 switch (fType) {
257 case Type::kEmpty:
258 ret = false;
259 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400260 case Type::kInvertedEmpty:
261 ret = true;
262 break;
bsalomon425c27f2016-06-23 13:18:45 -0700263 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700264 ret = fRRectData.fInverted;
bsalomon425c27f2016-06-23 13:18:45 -0700265 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700266 case Type::kLine:
267 ret = fLineData.fInverted;
268 break;
bsalomon425c27f2016-06-23 13:18:45 -0700269 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700270 ret = this->path().isInverseFillType();
bsalomon425c27f2016-06-23 13:18:45 -0700271 break;
272 }
273 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
274 SkASSERT(!(ret && this->style().isDashed()));
275 return ret;
276 }
277
278 /**
279 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
280 * because an arbitrary path effect could produce an inverse filled path. In other cases this
281 * can be thought of as "inverseFilledAfterStyling()".
282 */
283 bool mayBeInverseFilledAfterStyling() const {
284 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
285 // filled.
286 if (this->style().hasNonDashPathEffect()) {
287 return true;
288 }
289 return this->inverseFilled();
290 }
291
292 /**
bsalomon7c73a532016-05-11 15:15:56 -0700293 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
294 * not have any caps if stroked (modulo the effect of any path effect).
bsalomon06077562016-05-04 13:50:29 -0700295 */
296 bool knownToBeClosed() const {
297 switch (fType) {
298 case Type::kEmpty:
299 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400300 case Type::kInvertedEmpty:
301 return true;
bsalomon06077562016-05-04 13:50:29 -0700302 case Type::kRRect:
303 return true;
bsalomon0a0f67e2016-06-28 11:56:42 -0700304 case Type::kLine:
305 return false;
bsalomon06077562016-05-04 13:50:29 -0700306 case Type::kPath:
bsalomon425c27f2016-06-23 13:18:45 -0700307 // SkPath doesn't keep track of the closed status of each contour.
bsalomon728b0f72016-06-27 10:00:19 -0700308 return SkPathPriv::IsClosedSingleContour(this->path());
bsalomon06077562016-05-04 13:50:29 -0700309 }
310 return false;
311 }
312
bsalomon06115ee2016-06-07 06:28:51 -0700313 uint32_t segmentMask() const {
314 switch (fType) {
315 case Type::kEmpty:
316 return 0;
Brian Salomon085c0862017-08-31 15:44:51 -0400317 case Type::kInvertedEmpty:
318 return 0;
bsalomon06115ee2016-06-07 06:28:51 -0700319 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700320 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700321 return SkPath::kConic_SegmentMask;
bsalomon728b0f72016-06-27 10:00:19 -0700322 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700323 return SkPath::kLine_SegmentMask;
324 }
325 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
bsalomon0a0f67e2016-06-28 11:56:42 -0700326 case Type::kLine:
327 return SkPath::kLine_SegmentMask;
bsalomon06115ee2016-06-07 06:28:51 -0700328 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700329 return this->path().getSegmentMasks();
bsalomon06115ee2016-06-07 06:28:51 -0700330 }
331 return 0;
332 }
333
bsalomon06077562016-05-04 13:50:29 -0700334 /**
bsalomon47cc7692016-04-26 12:56:00 -0700335 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
336 * A negative value is returned if the shape has no key (shouldn't be cached).
337 */
338 int unstyledKeySize() const;
339
bsalomon425c27f2016-06-23 13:18:45 -0700340 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
341
bsalomon47cc7692016-04-26 12:56:00 -0700342 /**
343 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
344 * space allocated for the key and that unstyledKeySize() does not return a negative value
345 * for this shape.
346 */
347 void writeUnstyledKey(uint32_t* key) const;
348
349private:
bsalomon72dc51c2016-04-27 06:46:23 -0700350 enum class Type {
351 kEmpty,
Brian Salomon085c0862017-08-31 15:44:51 -0400352 kInvertedEmpty,
bsalomon72dc51c2016-04-27 06:46:23 -0700353 kRRect,
bsalomon0a0f67e2016-06-28 11:56:42 -0700354 kLine,
bsalomon72dc51c2016-04-27 06:46:23 -0700355 kPath,
356 };
357
bsalomon728b0f72016-06-27 10:00:19 -0700358 void initType(Type type, const SkPath* path = nullptr) {
359 fType = Type::kEmpty;
360 this->changeType(type, path);
361 }
362
363 void changeType(Type type, const SkPath* path = nullptr) {
364 bool wasPath = Type::kPath == fType;
365 fType = type;
366 bool isPath = Type::kPath == type;
367 SkASSERT(!path || isPath);
368 if (wasPath && !isPath) {
369 fPathData.fPath.~SkPath();
370 } else if (!wasPath && isPath) {
371 if (path) {
372 new (&fPathData.fPath) SkPath(*path);
373 } else {
374 new (&fPathData.fPath) SkPath();
375 }
376 } else if (isPath && path) {
377 fPathData.fPath = *path;
378 }
379 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
380 fPathData.fGenID = 0;
381 }
382
383 SkPath& path() {
384 SkASSERT(Type::kPath == fType);
385 return fPathData.fPath;
386 }
387
388 const SkPath& path() const {
389 SkASSERT(Type::kPath == fType);
390 return fPathData.fPath;
391 }
392
bsalomon97fd2d42016-05-09 13:02:01 -0700393 /** Constructor used by the applyStyle() function */
394 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700395
396 /**
397 * Determines the key we should inherit from the input shape's geometry and style when
398 * we are applying the style to create a new shape.
399 */
bsalomon97fd2d42016-05-09 13:02:01 -0700400 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700401
bsalomon1b28c1a2016-06-20 12:28:17 -0700402 void attemptToSimplifyPath();
bsalomon1b28c1a2016-06-20 12:28:17 -0700403 void attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700404 void attemptToSimplifyLine();
bsalomonee295642016-06-06 14:01:25 -0700405
bsalomona4817af2016-06-23 11:48:26 -0700406 // Defaults to use when there is no distinction between even/odd and winding fills.
407 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
408 static constexpr SkPath::FillType kDefaultPathInverseFillType =
409 SkPath::kInverseEvenOdd_FillType;
410
bsalomonee295642016-06-06 14:01:25 -0700411 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
412 static constexpr unsigned kDefaultRRectStart = 0;
413
414 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
415 SkPath::Direction* dir) {
416 *dir = kDefaultRRectDir;
417 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
418 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
419 if (!hasPathEffect) {
420 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
421 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700422 }
bsalomonee295642016-06-06 14:01:25 -0700423 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
424 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
425 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
426 bool swapX = rect.fLeft > rect.fRight;
427 bool swapY = rect.fTop > rect.fBottom;
428 if (swapX && swapY) {
429 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
430 return 2 * 2;
431 } else if (swapX) {
432 *dir = SkPath::kCCW_Direction;
433 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
434 return 2 * 1;
435 } else if (swapY) {
436 *dir = SkPath::kCCW_Direction;
437 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
438 return 2 * 3;
bsalomon72dc51c2016-04-27 06:46:23 -0700439 }
bsalomonee295642016-06-06 14:01:25 -0700440 return 0;
441 }
442
443 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
444 SkPath::Direction* dir) {
445 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
446 // clockwise beginning at starting index 6.
447 static constexpr unsigned kPathRRectStartIdx = 6;
448 *dir = kDefaultRRectDir;
449 if (!hasPathEffect) {
450 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
451 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700452 }
bsalomonee295642016-06-06 14:01:25 -0700453 return kPathRRectStartIdx;
bsalomon72dc51c2016-04-27 06:46:23 -0700454 }
455
456 Type fType;
bsalomon728b0f72016-06-27 10:00:19 -0700457 union {
458 struct {
459 SkRRect fRRect;
460 SkPath::Direction fDir;
461 unsigned fStart;
462 bool fInverted;
463 } fRRectData;
464 struct {
465 SkPath fPath;
466 // Gen ID of the original path (fPath may be modified)
467 int32_t fGenID;
468 } fPathData;
bsalomon0a0f67e2016-06-28 11:56:42 -0700469 struct {
470 SkPoint fPts[2];
471 bool fInverted;
472 } fLineData;
bsalomon728b0f72016-06-27 10:00:19 -0700473 };
bsalomon47cc7692016-04-26 12:56:00 -0700474 GrStyle fStyle;
475 SkAutoSTArray<8, uint32_t> fInheritedKey;
476};
477#endif