blob: a1828c5d603d1fd8c4d4cec09d0c0136fa33e050 [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
Robert Phillips20390c32018-08-17 11:01:03 -040055 GrShape(const SkRRect& rrect, const GrStyle& style) : fStyle(style) {
bsalomon728b0f72016-06-27 10:00:19 -070056 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
Robert Phillips20390c32018-08-17 11:01:03 -040084 GrShape(const SkRect& rect, const GrStyle& style) : fStyle(style) {
bsalomon728b0f72016-06-27 10:00:19 -070085 this->initType(Type::kRRect);
86 fRRectData.fRRect = SkRRect::MakeRect(rect);
87 fRRectData.fInverted = false;
88 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
89 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -070090 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -070091 }
bsalomon47cc7692016-04-26 12:56:00 -070092
Brian Salomonda6d0722018-01-03 13:54:35 -050093 GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
bsalomon728b0f72016-06-27 10:00:19 -070094 this->initType(Type::kPath, &path);
bsalomon1b28c1a2016-06-20 12:28:17 -070095 this->attemptToSimplifyPath();
bsalomon72dc51c2016-04-27 06:46:23 -070096 }
97
Robert Phillips20390c32018-08-17 11:01:03 -040098 GrShape(const SkRRect& rrect, const SkPaint& paint) : fStyle(paint) {
bsalomon728b0f72016-06-27 10:00:19 -070099 this->initType(Type::kRRect);
100 fRRectData.fRRect = rrect;
101 fRRectData.fInverted = false;
102 fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
103 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -0700104 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -0700105 }
bsalomon47cc7692016-04-26 12:56:00 -0700106
Robert Phillips20390c32018-08-17 11:01:03 -0400107 GrShape(const SkRect& rect, const SkPaint& paint) : fStyle(paint) {
bsalomon728b0f72016-06-27 10:00:19 -0700108 this->initType(Type::kRRect);
109 fRRectData.fRRect = SkRRect::MakeRect(rect);
110 fRRectData.fInverted = false;
111 fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
112 &fRRectData.fDir);
bsalomon1b28c1a2016-06-20 12:28:17 -0700113 this->attemptToSimplifyRRect();
bsalomon5e410b42016-04-28 09:30:46 -0700114 }
bsalomon47cc7692016-04-26 12:56:00 -0700115
Brian Salomone4949402018-04-26 15:22:04 -0400116 static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
117 SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
118
bsalomon47cc7692016-04-26 12:56:00 -0700119 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
Robert Phillips20390c32018-08-17 11:01:03 -0400154 bool isRect() const {
155 if (Type::kRRect != fType) {
156 return false;
157 }
158
159 return fRRectData.fRRect.isRect();
160 }
161
bsalomon7c73a532016-05-11 15:15:56 -0700162 /** Returns the unstyled geometry as a rrect if possible. */
bsalomon70493962016-06-10 08:05:14 -0700163 bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
bsalomon47cc7692016-04-26 12:56:00 -0700164 if (Type::kRRect != fType) {
165 return false;
166 }
167 if (rrect) {
bsalomon728b0f72016-06-27 10:00:19 -0700168 *rrect = fRRectData.fRRect;
bsalomon47cc7692016-04-26 12:56:00 -0700169 }
bsalomonee295642016-06-06 14:01:25 -0700170 if (dir) {
bsalomon728b0f72016-06-27 10:00:19 -0700171 *dir = fRRectData.fDir;
bsalomonee295642016-06-06 14:01:25 -0700172 }
173 if (start) {
bsalomon728b0f72016-06-27 10:00:19 -0700174 *start = fRRectData.fStart;
bsalomonee295642016-06-06 14:01:25 -0700175 }
bsalomon70493962016-06-10 08:05:14 -0700176 if (inverted) {
bsalomon728b0f72016-06-27 10:00:19 -0700177 *inverted = fRRectData.fInverted;
bsalomon70493962016-06-10 08:05:14 -0700178 }
bsalomon47cc7692016-04-26 12:56:00 -0700179 return true;
180 }
181
bsalomon398e3f42016-06-13 10:22:48 -0700182 /**
183 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
184 * An inverse filled line path is still considered a line.
185 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700186 bool asLine(SkPoint pts[2], bool* inverted) const {
187 if (fType != Type::kLine) {
188 return false;
189 }
190 if (pts) {
191 pts[0] = fLineData.fPts[0];
192 pts[1] = fLineData.fPts[1];
193 }
194 if (inverted) {
195 *inverted = fLineData.fInverted;
196 }
197 return true;
198 }
bsalomon398e3f42016-06-13 10:22:48 -0700199
bsalomon7c73a532016-05-11 15:15:56 -0700200 /** Returns the unstyled geometry as a path. */
bsalomon47cc7692016-04-26 12:56:00 -0700201 void asPath(SkPath* out) const {
202 switch (fType) {
bsalomon06077562016-05-04 13:50:29 -0700203 case Type::kEmpty:
204 out->reset();
205 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400206 case Type::kInvertedEmpty:
207 out->reset();
208 out->setFillType(kDefaultPathInverseFillType);
209 break;
bsalomon47cc7692016-04-26 12:56:00 -0700210 case Type::kRRect:
211 out->reset();
bsalomon728b0f72016-06-27 10:00:19 -0700212 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
bsalomon93f66bc2016-06-21 08:35:49 -0700213 // Below matches the fill type that attemptToSimplifyPath uses.
bsalomon728b0f72016-06-27 10:00:19 -0700214 if (fRRectData.fInverted) {
bsalomona4817af2016-06-23 11:48:26 -0700215 out->setFillType(kDefaultPathInverseFillType);
bsalomon93f66bc2016-06-21 08:35:49 -0700216 } else {
bsalomona4817af2016-06-23 11:48:26 -0700217 out->setFillType(kDefaultPathFillType);
bsalomon70493962016-06-10 08:05:14 -0700218 }
bsalomon47cc7692016-04-26 12:56:00 -0700219 break;
Brian Salomone4949402018-04-26 15:22:04 -0400220 case Type::kArc:
221 SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
222 fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
223 fStyle.isSimpleFill());
224 if (fArcData.fInverted) {
225 out->setFillType(kDefaultPathInverseFillType);
226 } else {
227 out->setFillType(kDefaultPathFillType);
228 }
229 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700230 case Type::kLine:
231 out->reset();
232 out->moveTo(fLineData.fPts[0]);
233 out->lineTo(fLineData.fPts[1]);
234 if (fLineData.fInverted) {
235 out->setFillType(kDefaultPathInverseFillType);
236 } else {
237 out->setFillType(kDefaultPathFillType);
238 }
239 break;
bsalomon47cc7692016-04-26 12:56:00 -0700240 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700241 *out = this->path();
bsalomon47cc7692016-04-26 12:56:00 -0700242 break;
bsalomon47cc7692016-04-26 12:56:00 -0700243 }
244 }
245
Robert Phillips73653b42018-08-22 12:42:42 -0400246 // Can this shape be drawn as a pair of filled nested rectangles?
247 bool asNestedRects(SkRect rects[2]) const {
248 if (Type::kPath != fType) {
249 return false;
250 }
251
252 // TODO: it would be better two store DRRects natively in the shape rather than converting
253 // them to a path and then reextracting the nested rects
254 if (this->path().isInverseFillType()) {
255 return false;
256 }
257
258 SkPath::Direction dirs[2];
259 if (!this->path().isNestedFillRects(rects, dirs)) {
260 return false;
261 }
262
263 if (SkPath::kWinding_FillType == this->path().getFillType() && dirs[0] == dirs[1]) {
264 // The two rects need to be wound opposite to each other
265 return false;
266 }
267
268 // Right now, nested rects where the margin is not the same width
269 // all around do not render correctly
270 const SkScalar* outer = rects[0].asScalars();
271 const SkScalar* inner = rects[1].asScalars();
272
273 bool allEq = true;
274
275 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
276 bool allGoE1 = margin >= SK_Scalar1;
277
278 for (int i = 1; i < 4; ++i) {
279 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
280 if (temp < SK_Scalar1) {
281 allGoE1 = false;
282 }
283 if (!SkScalarNearlyEqual(margin, temp)) {
284 allEq = false;
285 }
286 }
287
288 return allEq || allGoE1;
289 }
290
bsalomon47cc7692016-04-26 12:56:00 -0700291 /**
bsalomon7c73a532016-05-11 15:15:56 -0700292 * Returns whether the geometry is empty. Note that applying the style could produce a
Brian Salomon085c0862017-08-31 15:44:51 -0400293 * non-empty shape. It also may have an inverse fill.
bsalomon7c73a532016-05-11 15:15:56 -0700294 */
Brian Salomon085c0862017-08-31 15:44:51 -0400295 bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
bsalomon7c73a532016-05-11 15:15:56 -0700296
bsalomon70493962016-06-10 08:05:14 -0700297 /**
298 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
299 * the inverse fill nature of the geometry.
300 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700301 SkRect bounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700302
bsalomon70493962016-06-10 08:05:14 -0700303 /**
304 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
305 * status).
306 */
bsalomon0a0f67e2016-06-28 11:56:42 -0700307 SkRect styledBounds() const;
bsalomon9fb42032016-05-13 09:23:38 -0700308
bsalomon7c73a532016-05-11 15:15:56 -0700309 /**
bsalomon425c27f2016-06-23 13:18:45 -0700310 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
311 * convex path is considered to be closed if they styling reflects a fill and not otherwise.
312 * This is because filling closes all contours in the path.
313 */
314 bool knownToBeConvex() const {
315 switch (fType) {
316 case Type::kEmpty:
317 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400318 case Type::kInvertedEmpty:
319 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700320 case Type::kRRect:
321 return true;
Brian Salomone4949402018-04-26 15:22:04 -0400322 case Type::kArc:
323 return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
324 SkToBool(fArcData.fUseCenter),
325 fStyle.isSimpleFill());
bsalomon0a0f67e2016-06-28 11:56:42 -0700326 case Type::kLine:
327 return true;
bsalomon425c27f2016-06-23 13:18:45 -0700328 case Type::kPath:
329 // SkPath.isConvex() really means "is this path convex were it to be closed" and
330 // thus doesn't give the correct answer for stroked paths, hence we also check
331 // whether the path is either filled or closed. Convex paths may only have one
332 // contour hence isLastContourClosed() is a sufficient for a convex path.
bsalomon728b0f72016-06-27 10:00:19 -0700333 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
334 this->path().isConvex();
bsalomon425c27f2016-06-23 13:18:45 -0700335 }
336 return false;
337 }
338
339 /** Is the pre-styled geometry inverse filled? */
340 bool inverseFilled() const {
341 bool ret = false;
342 switch (fType) {
343 case Type::kEmpty:
344 ret = false;
345 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400346 case Type::kInvertedEmpty:
347 ret = true;
348 break;
bsalomon425c27f2016-06-23 13:18:45 -0700349 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700350 ret = fRRectData.fInverted;
bsalomon425c27f2016-06-23 13:18:45 -0700351 break;
Brian Salomone4949402018-04-26 15:22:04 -0400352 case Type::kArc:
353 ret = fArcData.fInverted;
354 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700355 case Type::kLine:
356 ret = fLineData.fInverted;
357 break;
bsalomon425c27f2016-06-23 13:18:45 -0700358 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700359 ret = this->path().isInverseFillType();
bsalomon425c27f2016-06-23 13:18:45 -0700360 break;
361 }
362 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
363 SkASSERT(!(ret && this->style().isDashed()));
364 return ret;
365 }
366
367 /**
368 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
369 * because an arbitrary path effect could produce an inverse filled path. In other cases this
370 * can be thought of as "inverseFilledAfterStyling()".
371 */
372 bool mayBeInverseFilledAfterStyling() const {
373 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
374 // filled.
375 if (this->style().hasNonDashPathEffect()) {
376 return true;
377 }
378 return this->inverseFilled();
379 }
380
381 /**
bsalomon7c73a532016-05-11 15:15:56 -0700382 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
383 * not have any caps if stroked (modulo the effect of any path effect).
bsalomon06077562016-05-04 13:50:29 -0700384 */
385 bool knownToBeClosed() const {
386 switch (fType) {
387 case Type::kEmpty:
388 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400389 case Type::kInvertedEmpty:
390 return true;
bsalomon06077562016-05-04 13:50:29 -0700391 case Type::kRRect:
392 return true;
Brian Salomone4949402018-04-26 15:22:04 -0400393 case Type::kArc:
394 return fArcData.fUseCenter;
bsalomon0a0f67e2016-06-28 11:56:42 -0700395 case Type::kLine:
396 return false;
bsalomon06077562016-05-04 13:50:29 -0700397 case Type::kPath:
bsalomon425c27f2016-06-23 13:18:45 -0700398 // SkPath doesn't keep track of the closed status of each contour.
bsalomon728b0f72016-06-27 10:00:19 -0700399 return SkPathPriv::IsClosedSingleContour(this->path());
bsalomon06077562016-05-04 13:50:29 -0700400 }
401 return false;
402 }
403
bsalomon06115ee2016-06-07 06:28:51 -0700404 uint32_t segmentMask() const {
405 switch (fType) {
406 case Type::kEmpty:
407 return 0;
Brian Salomon085c0862017-08-31 15:44:51 -0400408 case Type::kInvertedEmpty:
409 return 0;
bsalomon06115ee2016-06-07 06:28:51 -0700410 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700411 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700412 return SkPath::kConic_SegmentMask;
Brian Salomon2fad74a2017-12-20 13:28:55 -0500413 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
414 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700415 return SkPath::kLine_SegmentMask;
416 }
417 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
Brian Salomone4949402018-04-26 15:22:04 -0400418 case Type::kArc:
419 if (fArcData.fUseCenter) {
420 return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
421 }
422 return SkPath::kConic_SegmentMask;
bsalomon0a0f67e2016-06-28 11:56:42 -0700423 case Type::kLine:
424 return SkPath::kLine_SegmentMask;
bsalomon06115ee2016-06-07 06:28:51 -0700425 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700426 return this->path().getSegmentMasks();
bsalomon06115ee2016-06-07 06:28:51 -0700427 }
428 return 0;
429 }
430
bsalomon06077562016-05-04 13:50:29 -0700431 /**
bsalomon47cc7692016-04-26 12:56:00 -0700432 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
433 * A negative value is returned if the shape has no key (shouldn't be cached).
434 */
435 int unstyledKeySize() const;
436
bsalomon425c27f2016-06-23 13:18:45 -0700437 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
438
bsalomon47cc7692016-04-26 12:56:00 -0700439 /**
440 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
441 * space allocated for the key and that unstyledKeySize() does not return a negative value
442 * for this shape.
443 */
444 void writeUnstyledKey(uint32_t* key) const;
445
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400446 /**
447 * Adds a listener to the *original* path. Typically used to invalidate cached resources when
448 * 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 -0600449 * does nothing.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400450 */
Chris Daltonafa11582018-06-08 12:00:44 -0600451 void addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener>) const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400452
453 /**
Brian Osmanb379dcd2017-10-04 15:44:05 -0400454 * 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 -0500455 * the generation ID of the *original* path. This is the path that will receive
456 * GenIDChangeListeners added to this shape.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400457 */
458 uint32_t testingOnly_getOriginalGenerationID() const;
Brian Osmanb379dcd2017-10-04 15:44:05 -0400459 bool testingOnly_isPath() const;
Brian Salomonda6d0722018-01-03 13:54:35 -0500460 bool testingOnly_isNonVolatilePath() const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400461
bsalomon47cc7692016-04-26 12:56:00 -0700462private:
bsalomon72dc51c2016-04-27 06:46:23 -0700463 enum class Type {
464 kEmpty,
Brian Salomon085c0862017-08-31 15:44:51 -0400465 kInvertedEmpty,
bsalomon72dc51c2016-04-27 06:46:23 -0700466 kRRect,
Brian Salomone4949402018-04-26 15:22:04 -0400467 kArc,
bsalomon0a0f67e2016-06-28 11:56:42 -0700468 kLine,
bsalomon72dc51c2016-04-27 06:46:23 -0700469 kPath,
470 };
471
bsalomon728b0f72016-06-27 10:00:19 -0700472 void initType(Type type, const SkPath* path = nullptr) {
473 fType = Type::kEmpty;
474 this->changeType(type, path);
475 }
476
477 void changeType(Type type, const SkPath* path = nullptr) {
478 bool wasPath = Type::kPath == fType;
479 fType = type;
480 bool isPath = Type::kPath == type;
481 SkASSERT(!path || isPath);
482 if (wasPath && !isPath) {
483 fPathData.fPath.~SkPath();
484 } else if (!wasPath && isPath) {
485 if (path) {
486 new (&fPathData.fPath) SkPath(*path);
487 } else {
488 new (&fPathData.fPath) SkPath();
489 }
490 } else if (isPath && path) {
491 fPathData.fPath = *path;
492 }
493 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
494 fPathData.fGenID = 0;
495 }
496
497 SkPath& path() {
498 SkASSERT(Type::kPath == fType);
499 return fPathData.fPath;
500 }
501
502 const SkPath& path() const {
503 SkASSERT(Type::kPath == fType);
504 return fPathData.fPath;
505 }
506
bsalomon97fd2d42016-05-09 13:02:01 -0700507 /** Constructor used by the applyStyle() function */
508 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700509
510 /**
511 * Determines the key we should inherit from the input shape's geometry and style when
512 * we are applying the style to create a new shape.
513 */
bsalomon97fd2d42016-05-09 13:02:01 -0700514 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700515
bsalomon1b28c1a2016-06-20 12:28:17 -0700516 void attemptToSimplifyPath();
bsalomon1b28c1a2016-06-20 12:28:17 -0700517 void attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700518 void attemptToSimplifyLine();
Brian Salomone4949402018-04-26 15:22:04 -0400519 void attemptToSimplifyArc();
bsalomonee295642016-06-06 14:01:25 -0700520
Brian Salomon72f78c32017-12-21 11:56:42 -0500521 bool attemptToSimplifyStrokedLineToRRect();
522
Brian Salomonda6d0722018-01-03 13:54:35 -0500523 /** Gets the path that gen id listeners should be added to. */
524 const SkPath* originalPathForListeners() const;
525
bsalomona4817af2016-06-23 11:48:26 -0700526 // Defaults to use when there is no distinction between even/odd and winding fills.
527 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
528 static constexpr SkPath::FillType kDefaultPathInverseFillType =
529 SkPath::kInverseEvenOdd_FillType;
530
bsalomonee295642016-06-06 14:01:25 -0700531 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
532 static constexpr unsigned kDefaultRRectStart = 0;
533
534 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
535 SkPath::Direction* dir) {
536 *dir = kDefaultRRectDir;
537 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
538 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
539 if (!hasPathEffect) {
540 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
541 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700542 }
bsalomonee295642016-06-06 14:01:25 -0700543 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
544 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
545 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
546 bool swapX = rect.fLeft > rect.fRight;
547 bool swapY = rect.fTop > rect.fBottom;
548 if (swapX && swapY) {
549 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
550 return 2 * 2;
551 } else if (swapX) {
552 *dir = SkPath::kCCW_Direction;
553 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
554 return 2 * 1;
555 } else if (swapY) {
556 *dir = SkPath::kCCW_Direction;
557 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
558 return 2 * 3;
bsalomon72dc51c2016-04-27 06:46:23 -0700559 }
bsalomonee295642016-06-06 14:01:25 -0700560 return 0;
561 }
562
563 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
564 SkPath::Direction* dir) {
565 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
566 // clockwise beginning at starting index 6.
567 static constexpr unsigned kPathRRectStartIdx = 6;
568 *dir = kDefaultRRectDir;
569 if (!hasPathEffect) {
570 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
571 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700572 }
bsalomonee295642016-06-06 14:01:25 -0700573 return kPathRRectStartIdx;
bsalomon72dc51c2016-04-27 06:46:23 -0700574 }
575
bsalomon728b0f72016-06-27 10:00:19 -0700576 union {
577 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400578 SkRRect fRRect;
579 SkPath::Direction fDir;
580 unsigned fStart;
581 bool fInverted;
bsalomon728b0f72016-06-27 10:00:19 -0700582 } fRRectData;
583 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400584 SkRect fOval;
585 SkScalar fStartAngleDegrees;
586 SkScalar fSweepAngleDegrees;
587 int16_t fUseCenter;
588 int16_t fInverted;
589 } fArcData;
590 struct {
591 SkPath fPath;
bsalomon728b0f72016-06-27 10:00:19 -0700592 // Gen ID of the original path (fPath may be modified)
Brian Salomone4949402018-04-26 15:22:04 -0400593 int32_t fGenID;
bsalomon728b0f72016-06-27 10:00:19 -0700594 } fPathData;
bsalomon0a0f67e2016-06-28 11:56:42 -0700595 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400596 SkPoint fPts[2];
597 bool fInverted;
bsalomon0a0f67e2016-06-28 11:56:42 -0700598 } fLineData;
bsalomon728b0f72016-06-27 10:00:19 -0700599 };
Brian Salomone4949402018-04-26 15:22:04 -0400600 GrStyle fStyle;
601 SkTLazy<SkPath> fInheritedPathForListeners;
bsalomon47cc7692016-04-26 12:56:00 -0700602 SkAutoSTArray<8, uint32_t> fInheritedKey;
Brian Salomone4949402018-04-26 15:22:04 -0400603 Type fType;
bsalomon47cc7692016-04-26 12:56:00 -0700604};
605#endif