blob: 37f38f8844c63b9262847af659e136d9f8298f0c [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkPath.h"
12#include "include/core/SkRRect.h"
13#include "include/private/SkTemplates.h"
14#include "src/core/SkPathPriv.h"
15#include "src/core/SkTLazy.h"
16#include "src/gpu/GrStyle.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
Mike Reed4241f5e2019-09-14 19:13:23 +000064 GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
bsalomon70493962016-06-10 08:05:14 -070065 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. */
Mike Reed4241f5e2019-09-14 19:13:23 +0000163 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
Mike Reed4241f5e2019-09-14 19:13:23 +0000258 SkPath::Direction dirs[2];
Mike Reed430470d2019-09-12 17:09:12 -0400259 if (!SkPathPriv::IsNestedFillRects(this->path(), rects, dirs)) {
Robert Phillips73653b42018-08-22 12:42:42 -0400260 return false;
261 }
262
Mike Reed4241f5e2019-09-14 19:13:23 +0000263 if (SkPath::kWinding_FillType == this->path().getFillType() && dirs[0] == dirs[1]) {
Robert Phillips73653b42018-08-22 12:42:42 -0400264 // 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
Michael Ludwig0a1e9ef2019-08-30 10:03:15 -0400339 /**
340 * Does the shape have a known winding direction. Some degenerate convex shapes may not have
341 * a computable direction, but this is not always a requirement for path renderers so it is
342 * kept separate from knownToBeConvex().
343 */
344 bool knownDirection() const {
345 switch (fType) {
346 case Type::kEmpty:
347 return true;
348 case Type::kInvertedEmpty:
349 return true;
350 case Type::kRRect:
351 return true;
352 case Type::kArc:
353 return true;
354 case Type::kLine:
355 return true;
356 case Type::kPath:
357 // Assuming this is called after knownToBeConvex(), this should just be relying on
358 // cached convexity and direction and will be cheap.
359 return !SkPathPriv::CheapIsFirstDirection(this->path(),
360 SkPathPriv::kUnknown_FirstDirection);
361 }
362 return false;
363 }
364
bsalomon425c27f2016-06-23 13:18:45 -0700365 /** Is the pre-styled geometry inverse filled? */
366 bool inverseFilled() const {
367 bool ret = false;
368 switch (fType) {
369 case Type::kEmpty:
370 ret = false;
371 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400372 case Type::kInvertedEmpty:
373 ret = true;
374 break;
bsalomon425c27f2016-06-23 13:18:45 -0700375 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700376 ret = fRRectData.fInverted;
bsalomon425c27f2016-06-23 13:18:45 -0700377 break;
Brian Salomone4949402018-04-26 15:22:04 -0400378 case Type::kArc:
379 ret = fArcData.fInverted;
380 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700381 case Type::kLine:
382 ret = fLineData.fInverted;
383 break;
bsalomon425c27f2016-06-23 13:18:45 -0700384 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700385 ret = this->path().isInverseFillType();
bsalomon425c27f2016-06-23 13:18:45 -0700386 break;
387 }
388 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
389 SkASSERT(!(ret && this->style().isDashed()));
390 return ret;
391 }
392
393 /**
394 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
395 * because an arbitrary path effect could produce an inverse filled path. In other cases this
396 * can be thought of as "inverseFilledAfterStyling()".
397 */
398 bool mayBeInverseFilledAfterStyling() const {
399 // An arbitrary path effect can produce an arbitrary output path, which may be inverse
400 // filled.
401 if (this->style().hasNonDashPathEffect()) {
402 return true;
403 }
404 return this->inverseFilled();
405 }
406
407 /**
bsalomon7c73a532016-05-11 15:15:56 -0700408 * Is it known that the unstyled geometry has no unclosed contours. This means that it will
409 * not have any caps if stroked (modulo the effect of any path effect).
bsalomon06077562016-05-04 13:50:29 -0700410 */
411 bool knownToBeClosed() const {
412 switch (fType) {
413 case Type::kEmpty:
414 return true;
Brian Salomon085c0862017-08-31 15:44:51 -0400415 case Type::kInvertedEmpty:
416 return true;
bsalomon06077562016-05-04 13:50:29 -0700417 case Type::kRRect:
418 return true;
Brian Salomone4949402018-04-26 15:22:04 -0400419 case Type::kArc:
420 return fArcData.fUseCenter;
bsalomon0a0f67e2016-06-28 11:56:42 -0700421 case Type::kLine:
422 return false;
bsalomon06077562016-05-04 13:50:29 -0700423 case Type::kPath:
bsalomon425c27f2016-06-23 13:18:45 -0700424 // SkPath doesn't keep track of the closed status of each contour.
bsalomon728b0f72016-06-27 10:00:19 -0700425 return SkPathPriv::IsClosedSingleContour(this->path());
bsalomon06077562016-05-04 13:50:29 -0700426 }
427 return false;
428 }
429
bsalomon06115ee2016-06-07 06:28:51 -0700430 uint32_t segmentMask() const {
431 switch (fType) {
432 case Type::kEmpty:
433 return 0;
Brian Salomon085c0862017-08-31 15:44:51 -0400434 case Type::kInvertedEmpty:
435 return 0;
bsalomon06115ee2016-06-07 06:28:51 -0700436 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700437 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700438 return SkPath::kConic_SegmentMask;
Brian Salomon2fad74a2017-12-20 13:28:55 -0500439 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
440 fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
bsalomon06115ee2016-06-07 06:28:51 -0700441 return SkPath::kLine_SegmentMask;
442 }
443 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
Brian Salomone4949402018-04-26 15:22:04 -0400444 case Type::kArc:
445 if (fArcData.fUseCenter) {
446 return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
447 }
448 return SkPath::kConic_SegmentMask;
bsalomon0a0f67e2016-06-28 11:56:42 -0700449 case Type::kLine:
450 return SkPath::kLine_SegmentMask;
bsalomon06115ee2016-06-07 06:28:51 -0700451 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700452 return this->path().getSegmentMasks();
bsalomon06115ee2016-06-07 06:28:51 -0700453 }
454 return 0;
455 }
456
bsalomon06077562016-05-04 13:50:29 -0700457 /**
bsalomon47cc7692016-04-26 12:56:00 -0700458 * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
459 * A negative value is returned if the shape has no key (shouldn't be cached).
460 */
461 int unstyledKeySize() const;
462
bsalomon425c27f2016-06-23 13:18:45 -0700463 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
464
bsalomon47cc7692016-04-26 12:56:00 -0700465 /**
466 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
467 * space allocated for the key and that unstyledKeySize() does not return a negative value
468 * for this shape.
469 */
470 void writeUnstyledKey(uint32_t* key) const;
471
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400472 /**
473 * Adds a listener to the *original* path. Typically used to invalidate cached resources when
474 * 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 -0600475 * does nothing.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400476 */
Chris Daltonafa11582018-06-08 12:00:44 -0600477 void addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener>) const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400478
479 /**
Brian Osmanb379dcd2017-10-04 15:44:05 -0400480 * 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 -0500481 * the generation ID of the *original* path. This is the path that will receive
482 * GenIDChangeListeners added to this shape.
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400483 */
484 uint32_t testingOnly_getOriginalGenerationID() const;
Brian Osmanb379dcd2017-10-04 15:44:05 -0400485 bool testingOnly_isPath() const;
Brian Salomonda6d0722018-01-03 13:54:35 -0500486 bool testingOnly_isNonVolatilePath() const;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400487
bsalomon47cc7692016-04-26 12:56:00 -0700488private:
bsalomon72dc51c2016-04-27 06:46:23 -0700489 enum class Type {
490 kEmpty,
Brian Salomon085c0862017-08-31 15:44:51 -0400491 kInvertedEmpty,
bsalomon72dc51c2016-04-27 06:46:23 -0700492 kRRect,
Brian Salomone4949402018-04-26 15:22:04 -0400493 kArc,
bsalomon0a0f67e2016-06-28 11:56:42 -0700494 kLine,
bsalomon72dc51c2016-04-27 06:46:23 -0700495 kPath,
496 };
497
bsalomon728b0f72016-06-27 10:00:19 -0700498 void initType(Type type, const SkPath* path = nullptr) {
499 fType = Type::kEmpty;
500 this->changeType(type, path);
501 }
502
503 void changeType(Type type, const SkPath* path = nullptr) {
504 bool wasPath = Type::kPath == fType;
505 fType = type;
506 bool isPath = Type::kPath == type;
507 SkASSERT(!path || isPath);
508 if (wasPath && !isPath) {
509 fPathData.fPath.~SkPath();
510 } else if (!wasPath && isPath) {
511 if (path) {
512 new (&fPathData.fPath) SkPath(*path);
513 } else {
514 new (&fPathData.fPath) SkPath();
515 }
516 } else if (isPath && path) {
517 fPathData.fPath = *path;
518 }
519 // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
520 fPathData.fGenID = 0;
521 }
522
523 SkPath& path() {
524 SkASSERT(Type::kPath == fType);
525 return fPathData.fPath;
526 }
527
528 const SkPath& path() const {
529 SkASSERT(Type::kPath == fType);
530 return fPathData.fPath;
531 }
532
bsalomon97fd2d42016-05-09 13:02:01 -0700533 /** Constructor used by the applyStyle() function */
534 GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700535
536 /**
537 * Determines the key we should inherit from the input shape's geometry and style when
538 * we are applying the style to create a new shape.
539 */
bsalomon97fd2d42016-05-09 13:02:01 -0700540 void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
bsalomon47cc7692016-04-26 12:56:00 -0700541
bsalomon1b28c1a2016-06-20 12:28:17 -0700542 void attemptToSimplifyPath();
bsalomon1b28c1a2016-06-20 12:28:17 -0700543 void attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700544 void attemptToSimplifyLine();
Brian Salomone4949402018-04-26 15:22:04 -0400545 void attemptToSimplifyArc();
bsalomonee295642016-06-06 14:01:25 -0700546
Brian Salomon72f78c32017-12-21 11:56:42 -0500547 bool attemptToSimplifyStrokedLineToRRect();
548
Brian Salomonda6d0722018-01-03 13:54:35 -0500549 /** Gets the path that gen id listeners should be added to. */
550 const SkPath* originalPathForListeners() const;
551
bsalomona4817af2016-06-23 11:48:26 -0700552 // Defaults to use when there is no distinction between even/odd and winding fills.
Mike Reed4241f5e2019-09-14 19:13:23 +0000553 static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
554 static constexpr SkPath::FillType kDefaultPathInverseFillType =
555 SkPath::kInverseEvenOdd_FillType;
bsalomona4817af2016-06-23 11:48:26 -0700556
Mike Reed4241f5e2019-09-14 19:13:23 +0000557 static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
bsalomonee295642016-06-06 14:01:25 -0700558 static constexpr unsigned kDefaultRRectStart = 0;
559
560 static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
Mike Reed4241f5e2019-09-14 19:13:23 +0000561 SkPath::Direction* dir) {
bsalomonee295642016-06-06 14:01:25 -0700562 *dir = kDefaultRRectDir;
563 // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
564 // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
565 if (!hasPathEffect) {
566 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
567 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700568 }
bsalomonee295642016-06-06 14:01:25 -0700569 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
570 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
571 // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
572 bool swapX = rect.fLeft > rect.fRight;
573 bool swapY = rect.fTop > rect.fBottom;
574 if (swapX && swapY) {
575 // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
576 return 2 * 2;
577 } else if (swapX) {
Mike Reed4241f5e2019-09-14 19:13:23 +0000578 *dir = SkPath::kCCW_Direction;
bsalomonee295642016-06-06 14:01:25 -0700579 // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
580 return 2 * 1;
581 } else if (swapY) {
Mike Reed4241f5e2019-09-14 19:13:23 +0000582 *dir = SkPath::kCCW_Direction;
bsalomonee295642016-06-06 14:01:25 -0700583 // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
584 return 2 * 3;
bsalomon72dc51c2016-04-27 06:46:23 -0700585 }
bsalomonee295642016-06-06 14:01:25 -0700586 return 0;
587 }
588
589 static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
Mike Reed4241f5e2019-09-14 19:13:23 +0000590 SkPath::Direction* dir) {
bsalomonee295642016-06-06 14:01:25 -0700591 // This comes from SkPath's interface. The default for adding a SkRRect to a path is
592 // clockwise beginning at starting index 6.
593 static constexpr unsigned kPathRRectStartIdx = 6;
594 *dir = kDefaultRRectDir;
595 if (!hasPathEffect) {
596 // It doesn't matter what start we use, just be consistent to avoid redundant keys.
597 return kDefaultRRectStart;
bsalomon72dc51c2016-04-27 06:46:23 -0700598 }
bsalomonee295642016-06-06 14:01:25 -0700599 return kPathRRectStartIdx;
bsalomon72dc51c2016-04-27 06:46:23 -0700600 }
601
bsalomon728b0f72016-06-27 10:00:19 -0700602 union {
603 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400604 SkRRect fRRect;
Mike Reed4241f5e2019-09-14 19:13:23 +0000605 SkPath::Direction fDir;
Brian Salomone4949402018-04-26 15:22:04 -0400606 unsigned fStart;
607 bool fInverted;
bsalomon728b0f72016-06-27 10:00:19 -0700608 } fRRectData;
609 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400610 SkRect fOval;
611 SkScalar fStartAngleDegrees;
612 SkScalar fSweepAngleDegrees;
613 int16_t fUseCenter;
614 int16_t fInverted;
615 } fArcData;
616 struct {
617 SkPath fPath;
bsalomon728b0f72016-06-27 10:00:19 -0700618 // Gen ID of the original path (fPath may be modified)
Brian Salomone4949402018-04-26 15:22:04 -0400619 int32_t fGenID;
bsalomon728b0f72016-06-27 10:00:19 -0700620 } fPathData;
bsalomon0a0f67e2016-06-28 11:56:42 -0700621 struct {
Brian Salomone4949402018-04-26 15:22:04 -0400622 SkPoint fPts[2];
623 bool fInverted;
bsalomon0a0f67e2016-06-28 11:56:42 -0700624 } fLineData;
bsalomon728b0f72016-06-27 10:00:19 -0700625 };
Brian Salomone4949402018-04-26 15:22:04 -0400626 GrStyle fStyle;
627 SkTLazy<SkPath> fInheritedPathForListeners;
bsalomon47cc7692016-04-26 12:56:00 -0700628 SkAutoSTArray<8, uint32_t> fInheritedKey;
Brian Salomone4949402018-04-26 15:22:04 -0400629 Type fType;
bsalomon47cc7692016-04-26 12:56:00 -0700630};
631#endif