blob: 8c0a0aec8ef87548b41a4763f48f25c69a8de81a [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#include "GrShape.h"
9
10GrShape& GrShape::operator=(const GrShape& that) {
bsalomon47cc7692016-04-26 12:56:00 -070011 fStyle = that.fStyle;
bsalomon728b0f72016-06-27 10:00:19 -070012 this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
bsalomon47cc7692016-04-26 12:56:00 -070013 switch (fType) {
14 case Type::kEmpty:
bsalomon47cc7692016-04-26 12:56:00 -070015 break;
Brian Salomon085c0862017-08-31 15:44:51 -040016 case Type::kInvertedEmpty:
17 break;
bsalomon47cc7692016-04-26 12:56:00 -070018 case Type::kRRect:
bsalomon0a0f67e2016-06-28 11:56:42 -070019 fRRectData = that.fRRectData;
20 break;
21 case Type::kLine:
22 fLineData = that.fLineData;
bsalomon47cc7692016-04-26 12:56:00 -070023 break;
24 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -070025 fPathData.fGenID = that.fPathData.fGenID;
bsalomon47cc7692016-04-26 12:56:00 -070026 break;
27 }
28 fInheritedKey.reset(that.fInheritedKey.count());
benjaminwagnerd9cca4a2016-05-04 11:06:19 -070029 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
30 sizeof(uint32_t) * fInheritedKey.count());
Brian Salomonda6d0722018-01-03 13:54:35 -050031 if (that.fInheritedPathForListeners.isValid()) {
32 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
33 } else {
34 fInheritedPathForListeners.reset();
35 }
bsalomon47cc7692016-04-26 12:56:00 -070036 return *this;
37}
38
Brian Salomon4f40caf2017-09-01 09:00:45 -040039static bool flip_inversion(bool originalIsInverted, GrShape::FillInversion inversion) {
40 switch (inversion) {
41 case GrShape::FillInversion::kPreserve:
42 return false;
43 case GrShape::FillInversion::kFlip:
44 return true;
45 case GrShape::FillInversion::kForceInverted:
46 return !originalIsInverted;
47 case GrShape::FillInversion::kForceNoninverted:
48 return originalIsInverted;
49 }
50 return false;
51}
52
53static bool is_inverted(bool originalIsInverted, GrShape::FillInversion inversion) {
54 switch (inversion) {
55 case GrShape::FillInversion::kPreserve:
56 return originalIsInverted;
57 case GrShape::FillInversion::kFlip:
58 return !originalIsInverted;
59 case GrShape::FillInversion::kForceInverted:
60 return true;
61 case GrShape::FillInversion::kForceNoninverted:
62 return false;
63 }
64 return false;
65}
66
67GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
68 if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) {
69 // By returning the original rather than falling through we can preserve any inherited style
70 // key. Otherwise, we wipe it out below since the style change invalidates it.
71 return original;
72 }
73 GrShape result;
Brian Salomonda6d0722018-01-03 13:54:35 -050074 if (original.fInheritedPathForListeners.isValid()) {
75 result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get());
76 }
Brian Salomon4f40caf2017-09-01 09:00:45 -040077 switch (original.fType) {
78 case Type::kRRect:
79 result.fType = original.fType;
80 result.fRRectData.fRRect = original.fRRectData.fRRect;
81 result.fRRectData.fDir = kDefaultRRectDir;
82 result.fRRectData.fStart = kDefaultRRectStart;
83 result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
84 break;
85 case Type::kLine:
86 // Lines don't fill.
87 if (is_inverted(original.fLineData.fInverted, inversion)) {
88 result.fType = Type::kInvertedEmpty;
89 } else {
90 result.fType = Type::kEmpty;
91 }
92 break;
93 case Type::kEmpty:
94 result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty : Type::kEmpty;
95 break;
96 case Type::kInvertedEmpty:
97 result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty : Type::kEmpty;
98 break;
99 case Type::kPath:
100 result.initType(Type::kPath, &original.fPathData.fPath);
101 result.fPathData.fGenID = original.fPathData.fGenID;
102 if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) {
103 result.fPathData.fPath.toggleInverseFillType();
104 }
105 if (!original.style().isSimpleFill()) {
106 // Going from a non-filled style to fill may allow additional simplifications (e.g.
107 // closing an open rect that wasn't closed in the original shape because it had
108 // stroke style).
109 result.attemptToSimplifyPath();
110 }
111 break;
112 }
113 // We don't copy the inherited key since it can contain path effect information that we just
114 // stripped.
115 return result;
116}
117
bsalomon0a0f67e2016-06-28 11:56:42 -0700118SkRect GrShape::bounds() const {
bsalomon0ae36a22016-07-18 07:31:13 -0700119 // Bounds where left == bottom or top == right can indicate a line or point shape. We return
120 // inverted bounds for a truly empty shape.
121 static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
bsalomon9fb42032016-05-13 09:23:38 -0700122 switch (fType) {
123 case Type::kEmpty:
bsalomon0ae36a22016-07-18 07:31:13 -0700124 return kInverted;
Brian Salomon085c0862017-08-31 15:44:51 -0400125 case Type::kInvertedEmpty:
126 return kInverted;
bsalomon0a0f67e2016-06-28 11:56:42 -0700127 case Type::kLine: {
128 SkRect bounds;
129 if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
130 bounds.fLeft = fLineData.fPts[0].fX;
131 bounds.fRight = fLineData.fPts[1].fX;
132 } else {
133 bounds.fLeft = fLineData.fPts[1].fX;
134 bounds.fRight = fLineData.fPts[0].fX;
135 }
136 if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) {
137 bounds.fTop = fLineData.fPts[0].fY;
138 bounds.fBottom = fLineData.fPts[1].fY;
139 } else {
140 bounds.fTop = fLineData.fPts[1].fY;
141 bounds.fBottom = fLineData.fPts[0].fY;
142 }
143 return bounds;
144 }
bsalomon9fb42032016-05-13 09:23:38 -0700145 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700146 return fRRectData.fRRect.getBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700147 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700148 return this->path().getBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700149 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400150 SK_ABORT("Unknown shape type");
bsalomon0ae36a22016-07-18 07:31:13 -0700151 return kInverted;
bsalomon9fb42032016-05-13 09:23:38 -0700152}
153
bsalomon0a0f67e2016-06-28 11:56:42 -0700154SkRect GrShape::styledBounds() const {
Brian Salomon085c0862017-08-31 15:44:51 -0400155 if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
bsalomon0a0f67e2016-06-28 11:56:42 -0700156 return SkRect::MakeEmpty();
bsalomon9fb42032016-05-13 09:23:38 -0700157 }
Brian Salomon085c0862017-08-31 15:44:51 -0400158
bsalomon0a0f67e2016-06-28 11:56:42 -0700159 SkRect bounds;
160 fStyle.adjustBounds(&bounds, this->bounds());
161 return bounds;
bsalomon9fb42032016-05-13 09:23:38 -0700162}
163
bsalomon67fa4e32016-09-21 08:26:57 -0700164// If the path is small enough to be keyed from its data this returns key length, otherwise -1.
165static int path_key_from_data_size(const SkPath& path) {
166 const int verbCnt = path.countVerbs();
167 if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) {
168 return -1;
169 }
170 const int pointCnt = path.countPoints();
171 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
172
173 GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
174 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
175 // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to
176 // a uint32_t length.
177 return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
178}
179
180// Writes the path data key into the passed pointer.
bsalomon0e4a4662016-09-21 11:23:46 -0700181static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
bsalomon67fa4e32016-09-21 08:26:57 -0700182 uint32_t* key = origKey;
183 // The check below should take care of negative values casted positive.
184 const int verbCnt = path.countVerbs();
185 const int pointCnt = path.countPoints();
186 const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
187 SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt);
188 SkASSERT(pointCnt && verbCnt);
189 *key++ = path.getFillType();
190 *key++ = verbCnt;
191 memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
192 int verbKeySize = SkAlign4(verbCnt);
193 // pad out to uint32_t alignment using value that will stand out when debugging.
194 uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
195 memset(pad, 0xDE, verbKeySize - verbCnt);
196 key += verbKeySize >> 2;
197
198 memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
199 GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
200 key += 2 * pointCnt;
bsalomon0e4a4662016-09-21 11:23:46 -0700201 sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
bsalomon67fa4e32016-09-21 08:26:57 -0700202 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
203 SkDEBUGCODE(key += conicWeightCnt);
204 SkASSERT(key - origKey == path_key_from_data_size(path));
205}
206
bsalomon47cc7692016-04-26 12:56:00 -0700207int GrShape::unstyledKeySize() const {
208 if (fInheritedKey.count()) {
209 return fInheritedKey.count();
210 }
211 switch (fType) {
212 case Type::kEmpty:
213 return 1;
Brian Salomon085c0862017-08-31 15:44:51 -0400214 case Type::kInvertedEmpty:
215 return 1;
bsalomon47cc7692016-04-26 12:56:00 -0700216 case Type::kRRect:
217 SkASSERT(!fInheritedKey.count());
218 SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
bsalomon70493962016-06-10 08:05:14 -0700219 // + 1 for the direction, start index, and inverseness.
bsalomonee295642016-06-06 14:01:25 -0700220 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
bsalomon0a0f67e2016-06-28 11:56:42 -0700221 case Type::kLine:
222 GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
223 // 4 for the end points and 1 for the inverseness
224 return 5;
bsalomon67fa4e32016-09-21 08:26:57 -0700225 case Type::kPath: {
bsalomonaa840642016-09-23 12:09:16 -0700226 if (0 == fPathData.fGenID) {
227 return -1;
228 }
bsalomon67fa4e32016-09-21 08:26:57 -0700229 int dataKeySize = path_key_from_data_size(fPathData.fPath);
230 if (dataKeySize >= 0) {
231 return dataKeySize;
232 }
bsalomon67fa4e32016-09-21 08:26:57 -0700233 // The key is the path ID and fill type.
234 return 2;
235 }
bsalomon47cc7692016-04-26 12:56:00 -0700236 }
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400237 SK_ABORT("Should never get here.");
bsalomon47cc7692016-04-26 12:56:00 -0700238 return 0;
239}
240
241void GrShape::writeUnstyledKey(uint32_t* key) const {
242 SkASSERT(this->unstyledKeySize());
243 SkDEBUGCODE(uint32_t* origKey = key;)
244 if (fInheritedKey.count()) {
245 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
246 SkDEBUGCODE(key += fInheritedKey.count();)
247 } else {
248 switch (fType) {
249 case Type::kEmpty:
250 *key++ = 1;
251 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400252 case Type::kInvertedEmpty:
253 *key++ = 2;
254 break;
bsalomon47cc7692016-04-26 12:56:00 -0700255 case Type::kRRect:
bsalomon728b0f72016-06-27 10:00:19 -0700256 fRRectData.fRRect.writeToMemory(key);
bsalomon47cc7692016-04-26 12:56:00 -0700257 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
bsalomon728b0f72016-06-27 10:00:19 -0700258 *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
259 *key |= fRRectData.fInverted ? (1 << 30) : 0;
260 *key++ |= fRRectData.fStart;
261 SkASSERT(fRRectData.fStart < 8);
bsalomon47cc7692016-04-26 12:56:00 -0700262 break;
bsalomon0a0f67e2016-06-28 11:56:42 -0700263 case Type::kLine:
264 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
265 key += 4;
266 *key++ = fLineData.fInverted ? 1 : 0;
267 break;
bsalomon67fa4e32016-09-21 08:26:57 -0700268 case Type::kPath: {
bsalomonaa840642016-09-23 12:09:16 -0700269 SkASSERT(fPathData.fGenID);
bsalomon67fa4e32016-09-21 08:26:57 -0700270 int dataKeySize = path_key_from_data_size(fPathData.fPath);
271 if (dataKeySize >= 0) {
bsalomon0e4a4662016-09-21 11:23:46 -0700272 write_path_key_from_data(fPathData.fPath, key);
bsalomon67fa4e32016-09-21 08:26:57 -0700273 return;
274 }
bsalomon728b0f72016-06-27 10:00:19 -0700275 *key++ = fPathData.fGenID;
bsalomon70493962016-06-10 08:05:14 -0700276 // We could canonicalize the fill rule for paths that don't differentiate between
277 // even/odd or winding fill (e.g. convex).
bsalomon728b0f72016-06-27 10:00:19 -0700278 *key++ = this->path().getFillType();
bsalomon47cc7692016-04-26 12:56:00 -0700279 break;
bsalomon67fa4e32016-09-21 08:26:57 -0700280 }
bsalomon47cc7692016-04-26 12:56:00 -0700281 }
282 }
283 SkASSERT(key - origKey == this->unstyledKeySize());
284}
285
bsalomon97fd2d42016-05-09 13:02:01 -0700286void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
bsalomon47cc7692016-04-26 12:56:00 -0700287 SkASSERT(!fInheritedKey.count());
288 // If the output shape turns out to be simple, then we will just use its geometric key
289 if (Type::kPath == fType) {
290 // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
291 // ApplyFullStyle(shape).
292 // The full key is structured as (geo,path_effect,stroke).
Brian Salomon4f40caf2017-09-01 09:00:45 -0400293 // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
bsalomon47cc7692016-04-26 12:56:00 -0700294 // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
295 // and then append the style key (which should now be stroke only) at the end.
296 int parentCnt = parent.fInheritedKey.count();
297 bool useParentGeoKey = !parentCnt;
298 if (useParentGeoKey) {
299 parentCnt = parent.unstyledKeySize();
bsalomon72dc51c2016-04-27 06:46:23 -0700300 if (parentCnt < 0) {
301 // The parent's geometry has no key so we will have no key.
bsalomon728b0f72016-06-27 10:00:19 -0700302 fPathData.fGenID = 0;
bsalomon72dc51c2016-04-27 06:46:23 -0700303 return;
304 }
bsalomon47cc7692016-04-26 12:56:00 -0700305 }
bsalomon06077562016-05-04 13:50:29 -0700306 uint32_t styleKeyFlags = 0;
307 if (parent.knownToBeClosed()) {
308 styleKeyFlags |= GrStyle::kClosed_KeyFlag;
309 }
bsalomon0ae36a22016-07-18 07:31:13 -0700310 if (parent.asLine(nullptr, nullptr)) {
311 styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
312 }
bsalomon06077562016-05-04 13:50:29 -0700313 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
bsalomon47cc7692016-04-26 12:56:00 -0700314 if (styleCnt < 0) {
bsalomon93f66bc2016-06-21 08:35:49 -0700315 // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
bsalomon47cc7692016-04-26 12:56:00 -0700316 // we try to get a key for the shape.
bsalomon728b0f72016-06-27 10:00:19 -0700317 fPathData.fGenID = 0;
bsalomon72dc51c2016-04-27 06:46:23 -0700318 return;
bsalomon47cc7692016-04-26 12:56:00 -0700319 }
bsalomon72dc51c2016-04-27 06:46:23 -0700320 fInheritedKey.reset(parentCnt + styleCnt);
321 if (useParentGeoKey) {
322 // This will be the geo key.
323 parent.writeUnstyledKey(fInheritedKey.get());
324 } else {
325 // This should be (geo,path_effect).
326 memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
327 parentCnt * sizeof(uint32_t));
328 }
329 // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
bsalomon97fd2d42016-05-09 13:02:01 -0700330 GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
331 styleKeyFlags);
bsalomon47cc7692016-04-26 12:56:00 -0700332 }
333}
334
Brian Salomonda6d0722018-01-03 13:54:35 -0500335const SkPath* GrShape::originalPathForListeners() const {
336 if (fInheritedPathForListeners.isValid()) {
337 return fInheritedPathForListeners.get();
338 } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) {
339 return &fPathData.fPath;
340 }
341 return nullptr;
Brian Osmanf6f7cf62017-09-25 16:49:55 -0400342}
343
Brian Salomonda6d0722018-01-03 13:54:35 -0500344void GrShape::addGenIDChangeListener(SkPathRef::GenIDChangeListener* listener) const {
345 if (const auto* lp = this->originalPathForListeners()) {
346 SkPathPriv::AddGenIDChangeListener(*lp, listener);
347 } else {
348 delete listener;
349 }
350}
351
352GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
bsalomon728b0f72016-06-27 10:00:19 -0700353 const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
354 this->initType(that.fType, thatPath);
bsalomon47cc7692016-04-26 12:56:00 -0700355 switch (fType) {
356 case Type::kEmpty:
bsalomon93f66bc2016-06-21 08:35:49 -0700357 break;
Brian Salomon085c0862017-08-31 15:44:51 -0400358 case Type::kInvertedEmpty:
359 break;
bsalomon47cc7692016-04-26 12:56:00 -0700360 case Type::kRRect:
bsalomon0a0f67e2016-06-28 11:56:42 -0700361 fRRectData = that.fRRectData;
362 break;
363 case Type::kLine:
364 fLineData = that.fLineData;
bsalomon93f66bc2016-06-21 08:35:49 -0700365 break;
bsalomon47cc7692016-04-26 12:56:00 -0700366 case Type::kPath:
bsalomon728b0f72016-06-27 10:00:19 -0700367 fPathData.fGenID = that.fPathData.fGenID;
bsalomon93f66bc2016-06-21 08:35:49 -0700368 break;
bsalomon47cc7692016-04-26 12:56:00 -0700369 }
370 fInheritedKey.reset(that.fInheritedKey.count());
bsalomon93f66bc2016-06-21 08:35:49 -0700371 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
372 sizeof(uint32_t) * fInheritedKey.count());
Brian Salomonda6d0722018-01-03 13:54:35 -0500373 if (that.fInheritedPathForListeners.isValid()) {
374 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
375 }
bsalomon47cc7692016-04-26 12:56:00 -0700376}
377
bsalomon97fd2d42016-05-09 13:02:01 -0700378GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
379 // TODO: Add some quantization of scale for better cache performance here or leave that up
380 // to caller?
381 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
382 // stroke of a rect).
bsalomonfb083272016-05-04 08:27:41 -0700383 if (!parent.style().applies() ||
384 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
bsalomon728b0f72016-06-27 10:00:19 -0700385 this->initType(Type::kEmpty);
bsalomonfb083272016-05-04 08:27:41 -0700386 *this = parent;
387 return;
388 }
389
bsalomon47cc7692016-04-26 12:56:00 -0700390 SkPathEffect* pe = parent.fStyle.pathEffect();
bsalomonfb083272016-05-04 08:27:41 -0700391 SkTLazy<SkPath> tmpPath;
392 const GrShape* parentForKey = &parent;
393 SkTLazy<GrShape> tmpParent;
bsalomon728b0f72016-06-27 10:00:19 -0700394 this->initType(Type::kPath);
395 fPathData.fGenID = 0;
bsalomon47cc7692016-04-26 12:56:00 -0700396 if (pe) {
bsalomon728b0f72016-06-27 10:00:19 -0700397 const SkPath* srcForPathEffect;
bsalomon47cc7692016-04-26 12:56:00 -0700398 if (parent.fType == Type::kPath) {
bsalomon728b0f72016-06-27 10:00:19 -0700399 srcForPathEffect = &parent.path();
bsalomon47cc7692016-04-26 12:56:00 -0700400 } else {
bsalomonfb083272016-05-04 08:27:41 -0700401 srcForPathEffect = tmpPath.init();
402 parent.asPath(tmpPath.get());
bsalomon47cc7692016-04-26 12:56:00 -0700403 }
404 // Should we consider bounds? Would have to include in key, but it'd be nice to know
405 // if the bounds actually modified anything before including in key.
bsalomonfb083272016-05-04 08:27:41 -0700406 SkStrokeRec strokeRec = parent.fStyle.strokeRec();
bsalomon728b0f72016-06-27 10:00:19 -0700407 if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
bsalomon398e3f42016-06-13 10:22:48 -0700408 scale)) {
bsalomon0ae36a22016-07-18 07:31:13 -0700409 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
410 *this = tmpParent.get()->applyStyle(apply, scale);
411 return;
bsalomon47cc7692016-04-26 12:56:00 -0700412 }
bsalomon97fd2d42016-05-09 13:02:01 -0700413 // A path effect has access to change the res scale but we aren't expecting it to and it
414 // would mess up our key computation.
415 SkASSERT(scale == strokeRec.getResScale());
bsalomon1b28c1a2016-06-20 12:28:17 -0700416 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
417 // The intermediate shape may not be a general path. If we we're just applying
418 // the path effect then attemptToReduceFromPath would catch it. This means that
419 // when we subsequently applied the remaining strokeRec we would have a non-path
420 // parent shape that would be used to determine the the stroked path's key.
421 // We detect that case here and change parentForKey to a temporary that represents
422 // the simpler shape so that applying both path effect and the strokerec all at
423 // once produces the same key.
bsalomon728b0f72016-06-27 10:00:19 -0700424 tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
bsalomon1b28c1a2016-06-20 12:28:17 -0700425 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
426 if (!tmpPath.isValid()) {
427 tmpPath.init();
bsalomon72dc51c2016-04-27 06:46:23 -0700428 }
bsalomon1b28c1a2016-06-20 12:28:17 -0700429 tmpParent.get()->asPath(tmpPath.get());
430 SkStrokeRec::InitStyle fillOrHairline;
bsalomon0ae36a22016-07-18 07:31:13 -0700431 // The parent shape may have simplified away the strokeRec, check for that here.
432 if (tmpParent.get()->style().applies()) {
433 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
434 *tmpPath.get(), scale));
435 } else if (tmpParent.get()->style().isSimpleFill()) {
436 fillOrHairline = SkStrokeRec::kFill_InitStyle;
437 } else {
438 SkASSERT(tmpParent.get()->style().isSimpleHairline());
439 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
440 }
bsalomon1b28c1a2016-06-20 12:28:17 -0700441 fStyle.resetToInitStyle(fillOrHairline);
442 parentForKey = tmpParent.get();
bsalomonfb083272016-05-04 08:27:41 -0700443 } else {
444 fStyle = GrStyle(strokeRec, nullptr);
bsalomon72dc51c2016-04-27 06:46:23 -0700445 }
bsalomon47cc7692016-04-26 12:56:00 -0700446 } else {
bsalomon97fd2d42016-05-09 13:02:01 -0700447 const SkPath* srcForParentStyle;
bsalomonfb083272016-05-04 08:27:41 -0700448 if (parent.fType == Type::kPath) {
bsalomon728b0f72016-06-27 10:00:19 -0700449 srcForParentStyle = &parent.path();
bsalomonfb083272016-05-04 08:27:41 -0700450 } else {
bsalomon97fd2d42016-05-09 13:02:01 -0700451 srcForParentStyle = tmpPath.init();
bsalomonfb083272016-05-04 08:27:41 -0700452 parent.asPath(tmpPath.get());
453 }
bsalomon97fd2d42016-05-09 13:02:01 -0700454 SkStrokeRec::InitStyle fillOrHairline;
455 SkASSERT(parent.fStyle.applies());
456 SkASSERT(!parent.fStyle.pathEffect());
bsalomon728b0f72016-06-27 10:00:19 -0700457 SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
bsalomon97fd2d42016-05-09 13:02:01 -0700458 scale));
459 fStyle.resetToInitStyle(fillOrHairline);
bsalomon47cc7692016-04-26 12:56:00 -0700460 }
Brian Salomonda6d0722018-01-03 13:54:35 -0500461 if (parent.fInheritedPathForListeners.isValid()) {
462 fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get());
463 } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) {
464 fInheritedPathForListeners.set(parent.fPathData.fPath);
465 }
bsalomon1b28c1a2016-06-20 12:28:17 -0700466 this->attemptToSimplifyPath();
bsalomon97fd2d42016-05-09 13:02:01 -0700467 this->setInheritedKey(*parentForKey, apply, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700468}
bsalomonee295642016-06-06 14:01:25 -0700469
bsalomon1b28c1a2016-06-20 12:28:17 -0700470void GrShape::attemptToSimplifyPath() {
bsalomonee295642016-06-06 14:01:25 -0700471 SkRect rect;
bsalomon728b0f72016-06-27 10:00:19 -0700472 SkRRect rrect;
473 SkPath::Direction rrectDir;
474 unsigned rrectStart;
475 bool inverted = this->path().isInverseFillType();
bsalomon0a0f67e2016-06-28 11:56:42 -0700476 SkPoint pts[2];
bsalomon728b0f72016-06-27 10:00:19 -0700477 if (this->path().isEmpty()) {
Brian Salomon085c0862017-08-31 15:44:51 -0400478 // Dashing ignores inverseness skbug.com/5421.
479 this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty
480 : Type::kEmpty);
bsalomon0a0f67e2016-06-28 11:56:42 -0700481 } else if (this->path().isLine(pts)) {
482 this->changeType(Type::kLine);
483 fLineData.fPts[0] = pts[0];
484 fLineData.fPts[1] = pts[1];
485 fLineData.fInverted = inverted;
bsalomon728b0f72016-06-27 10:00:19 -0700486 } else if (this->path().isRRect(&rrect, &rrectDir, &rrectStart)) {
487 this->changeType(Type::kRRect);
488 fRRectData.fRRect = rrect;
489 fRRectData.fDir = rrectDir;
490 fRRectData.fStart = rrectStart;
491 fRRectData.fInverted = inverted;
bsalomon728b0f72016-06-27 10:00:19 -0700492 SkASSERT(!fRRectData.fRRect.isEmpty());
bsalomon728b0f72016-06-27 10:00:19 -0700493 } else if (this->path().isOval(&rect, &rrectDir, &rrectStart)) {
494 this->changeType(Type::kRRect);
495 fRRectData.fRRect.setOval(rect);
496 fRRectData.fDir = rrectDir;
497 fRRectData.fInverted = inverted;
bsalomon1b28c1a2016-06-20 12:28:17 -0700498 // convert from oval indexing to rrect indexiing.
bsalomon728b0f72016-06-27 10:00:19 -0700499 fRRectData.fStart = 2 * rrectStart;
500 } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
501 this->changeType(Type::kRRect);
bsalomon1b28c1a2016-06-20 12:28:17 -0700502 // When there is a path effect we restrict rect detection to the narrower API that
503 // gives us the starting position. Otherwise, we will retry with the more aggressive
504 // isRect().
bsalomon728b0f72016-06-27 10:00:19 -0700505 fRRectData.fRRect.setRect(rect);
506 fRRectData.fInverted = inverted;
507 fRRectData.fDir = rrectDir;
bsalomon1b28c1a2016-06-20 12:28:17 -0700508 // convert from rect indexing to rrect indexiing.
bsalomon728b0f72016-06-27 10:00:19 -0700509 fRRectData.fStart = 2 * rrectStart;
bsalomon1b28c1a2016-06-20 12:28:17 -0700510 } else if (!this->style().hasPathEffect()) {
bsalomonee295642016-06-06 14:01:25 -0700511 bool closed;
bsalomon728b0f72016-06-27 10:00:19 -0700512 if (this->path().isRect(&rect, &closed, nullptr)) {
bsalomon1b28c1a2016-06-20 12:28:17 -0700513 if (closed || this->style().isSimpleFill()) {
bsalomon728b0f72016-06-27 10:00:19 -0700514 this->changeType(Type::kRRect);
515 fRRectData.fRRect.setRect(rect);
bsalomonee295642016-06-06 14:01:25 -0700516 // Since there is no path effect the dir and start index is immaterial.
bsalomon728b0f72016-06-27 10:00:19 -0700517 fRRectData.fDir = kDefaultRRectDir;
518 fRRectData.fStart = kDefaultRRectStart;
bsalomon1b28c1a2016-06-20 12:28:17 -0700519 // There isn't dashing so we will have to preserver inverseness.
bsalomon728b0f72016-06-27 10:00:19 -0700520 fRRectData.fInverted = inverted;
bsalomonee295642016-06-06 14:01:25 -0700521 }
522 }
523 }
bsalomon1b28c1a2016-06-20 12:28:17 -0700524 if (Type::kPath != fType) {
bsalomon1b28c1a2016-06-20 12:28:17 -0700525 fInheritedKey.reset(0);
Brian Osmanb379dcd2017-10-04 15:44:05 -0400526 // Whenever we simplify to a non-path, break the chain so we no longer refer to the
527 // original path. This prevents attaching genID listeners to temporary paths created when
528 // drawing simple shapes.
Brian Salomonda6d0722018-01-03 13:54:35 -0500529 fInheritedPathForListeners.reset();
bsalomon1b28c1a2016-06-20 12:28:17 -0700530 if (Type::kRRect == fType) {
531 this->attemptToSimplifyRRect();
bsalomon0a0f67e2016-06-28 11:56:42 -0700532 } else if (Type::kLine == fType) {
533 this->attemptToSimplifyLine();
bsalomon1b28c1a2016-06-20 12:28:17 -0700534 }
bsalomon93f66bc2016-06-21 08:35:49 -0700535 } else {
bsalomon728b0f72016-06-27 10:00:19 -0700536 if (fInheritedKey.count() || this->path().isVolatile()) {
537 fPathData.fGenID = 0;
bsalomon93f66bc2016-06-21 08:35:49 -0700538 } else {
bsalomon728b0f72016-06-27 10:00:19 -0700539 fPathData.fGenID = this->path().getGenerationID();
bsalomon93f66bc2016-06-21 08:35:49 -0700540 }
bsalomona4817af2016-06-23 11:48:26 -0700541 if (!this->style().hasNonDashPathEffect()) {
542 if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
543 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
544 // Stroke styles don't differentiate between winding and even/odd.
545 // Moreover, dashing ignores inverseness (skbug.com/5421)
bsalomon728b0f72016-06-27 10:00:19 -0700546 bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
bsalomona4817af2016-06-23 11:48:26 -0700547 if (inverse) {
bsalomon728b0f72016-06-27 10:00:19 -0700548 this->path().setFillType(kDefaultPathInverseFillType);
bsalomona4817af2016-06-23 11:48:26 -0700549 } else {
bsalomon728b0f72016-06-27 10:00:19 -0700550 this->path().setFillType(kDefaultPathFillType);
bsalomona4817af2016-06-23 11:48:26 -0700551 }
bsalomon728b0f72016-06-27 10:00:19 -0700552 } else if (this->path().isConvex()) {
bsalomona4817af2016-06-23 11:48:26 -0700553 // There is no distinction between even/odd and non-zero winding count for convex
554 // paths.
bsalomon728b0f72016-06-27 10:00:19 -0700555 if (this->path().isInverseFillType()) {
556 this->path().setFillType(kDefaultPathInverseFillType);
bsalomona4817af2016-06-23 11:48:26 -0700557 } else {
bsalomon728b0f72016-06-27 10:00:19 -0700558 this->path().setFillType(kDefaultPathFillType);
bsalomona4817af2016-06-23 11:48:26 -0700559 }
bsalomon93f66bc2016-06-21 08:35:49 -0700560 }
561 }
bsalomon1b28c1a2016-06-20 12:28:17 -0700562 }
563}
564
565void GrShape::attemptToSimplifyRRect() {
566 SkASSERT(Type::kRRect == fType);
567 SkASSERT(!fInheritedKey.count());
bsalomon728b0f72016-06-27 10:00:19 -0700568 if (fRRectData.fRRect.isEmpty()) {
Brian Salomon2fad74a2017-12-20 13:28:55 -0500569 // An empty filled rrect is equivalent to a filled empty path with inversion preserved.
570 if (fStyle.isSimpleFill()) {
571 fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty;
572 fStyle = GrStyle::SimpleFill();
573 return;
574 }
575 // Dashing a rrect with no width or height is equivalent to filling an emtpy path.
576 // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length
577 // line will produce cap geometry if the effect begins in an "on" interval.
578 if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) {
579 // Dashing ignores the inverseness (currently). skbug.com/5421.
580 fType = Type::kEmpty;
581 fStyle = GrStyle::SimpleFill();
582 return;
583 }
bsalomon1b28c1a2016-06-20 12:28:17 -0700584 }
585 if (!this->style().hasPathEffect()) {
bsalomon728b0f72016-06-27 10:00:19 -0700586 fRRectData.fDir = kDefaultRRectDir;
587 fRRectData.fStart = kDefaultRRectStart;
bsalomon1b28c1a2016-06-20 12:28:17 -0700588 } else if (fStyle.isDashed()) {
589 // Dashing ignores the inverseness (currently). skbug.com/5421
bsalomon728b0f72016-06-27 10:00:19 -0700590 fRRectData.fInverted = false;
bsalomon1b28c1a2016-06-20 12:28:17 -0700591 }
bsalomon487f8d32016-07-20 07:15:44 -0700592 // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
593 if (!fStyle.hasPathEffect() &&
594 fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style &&
595 fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
596 fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 &&
597 fRRectData.fRRect.isRect()) {
598 SkScalar r = fStyle.strokeRec().getWidth() / 2;
599 fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r));
600 fStyle = GrStyle::SimpleFill();
601 }
bsalomonee295642016-06-06 14:01:25 -0700602}
bsalomon0a0f67e2016-06-28 11:56:42 -0700603
604void GrShape::attemptToSimplifyLine() {
bsalomon0ae36a22016-07-18 07:31:13 -0700605 SkASSERT(Type::kLine == fType);
606 SkASSERT(!fInheritedKey.count());
607 if (fStyle.isDashed()) {
Brian Salomon72f78c32017-12-21 11:56:42 -0500608 bool allOffsZero = true;
609 for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) {
610 allOffsZero = !fStyle.dashIntervals()[i];
611 }
612 if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) {
613 return;
614 }
bsalomon0ae36a22016-07-18 07:31:13 -0700615 // Dashing ignores inverseness.
616 fLineData.fInverted = false;
617 return;
618 } else if (fStyle.hasPathEffect()) {
619 return;
620 }
621 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
622 // Make stroke + fill be stroke since the fill is empty.
623 SkStrokeRec rec = fStyle.strokeRec();
624 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
625 fStyle = GrStyle(rec, nullptr);
626 }
Brian Salomon085c0862017-08-31 15:44:51 -0400627 if (fStyle.isSimpleFill()) {
628 this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty);
bsalomon0ae36a22016-07-18 07:31:13 -0700629 return;
630 }
Brian Salomon72f78c32017-12-21 11:56:42 -0500631 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
632 this->attemptToSimplifyStrokedLineToRRect()) {
633 return;
bsalomon0a0f67e2016-06-28 11:56:42 -0700634 }
bsalomon0ae36a22016-07-18 07:31:13 -0700635 // Only path effects could care about the order of the points. Otherwise canonicalize
636 // the point order.
Brian Salomon72f78c32017-12-21 11:56:42 -0500637 SkPoint* pts = fLineData.fPts;
bsalomon0ae36a22016-07-18 07:31:13 -0700638 if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
639 SkTSwap(pts[0], pts[1]);
640 }
bsalomon0a0f67e2016-06-28 11:56:42 -0700641}
Brian Salomon72f78c32017-12-21 11:56:42 -0500642
643bool GrShape::attemptToSimplifyStrokedLineToRRect() {
644 SkASSERT(Type::kLine == fType);
645 SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
646
647 SkRect rect;
648 SkVector outset;
649 // If we allowed a rotation angle for rrects we could capture all cases here.
650 if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) {
651 rect.fLeft = SkTMin(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
652 rect.fRight = SkTMax(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
653 rect.fTop = rect.fBottom = fLineData.fPts[0].fY;
654 outset.fY = fStyle.strokeRec().getWidth() / 2.f;
655 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
656 } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) {
657 rect.fTop = SkTMin(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
658 rect.fBottom = SkTMax(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
659 rect.fLeft = rect.fRight = fLineData.fPts[0].fX;
660 outset.fX = fStyle.strokeRec().getWidth() / 2.f;
661 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
662 } else {
663 return false;
664 }
665 rect.outset(outset.fX, outset.fY);
666 if (rect.isEmpty()) {
667 this->changeType(Type::kEmpty);
668 fStyle = GrStyle::SimpleFill();
669 return true;
670 }
671 SkRRect rrect;
672 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
673 SkASSERT(outset.fX == outset.fY);
674 rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY);
675 } else {
676 rrect = SkRRect::MakeRect(rect);
677 }
678 bool inverted = fLineData.fInverted && !fStyle.hasPathEffect();
679 this->changeType(Type::kRRect);
680 fRRectData.fRRect = rrect;
681 fRRectData.fInverted = inverted;
682 fRRectData.fDir = kDefaultRRectDir;
683 fRRectData.fStart = kDefaultRRectStart;
684 fStyle = GrStyle::SimpleFill();
685 return true;
686}