blob: d2bec72cbfea4a6f51377339532a2159e6f0c37a [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) {
11 bool wasPath = Type::kPath == fType;
12 fStyle = that.fStyle;
13 fType = that.fType;
14 switch (fType) {
15 case Type::kEmpty:
16 if (wasPath) {
17 fPath.reset();
18 }
19 break;
20 case Type::kRRect:
21 if (wasPath) {
22 fPath.reset();
23 }
24 fRRect = that.fRRect;
bsalomonee295642016-06-06 14:01:25 -070025 fRRectDir = that.fRRectDir;
26 fRRectStart = that.fRRectStart;
bsalomon70493962016-06-10 08:05:14 -070027 fRRectIsInverted = that.fRRectIsInverted;
bsalomon47cc7692016-04-26 12:56:00 -070028 break;
29 case Type::kPath:
30 if (wasPath) {
31 *fPath.get() = *that.fPath.get();
32 } else {
33 fPath.set(*that.fPath.get());
34 }
35 break;
36 }
37 fInheritedKey.reset(that.fInheritedKey.count());
benjaminwagnerd9cca4a2016-05-04 11:06:19 -070038 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
39 sizeof(uint32_t) * fInheritedKey.count());
bsalomon47cc7692016-04-26 12:56:00 -070040 return *this;
41}
42
bsalomon9fb42032016-05-13 09:23:38 -070043const SkRect& GrShape::bounds() const {
44 static constexpr SkRect kEmpty = SkRect::MakeEmpty();
45 switch (fType) {
46 case Type::kEmpty:
47 return kEmpty;
48 case Type::kRRect:
49 return fRRect.getBounds();
50 case Type::kPath:
51 return fPath.get()->getBounds();
52 }
53 SkFAIL("Unknown shape type");
54 return kEmpty;
55}
56
57void GrShape::styledBounds(SkRect* bounds) const {
58 if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) {
59 *bounds = SkRect::MakeEmpty();
60 } else {
61 fStyle.adjustBounds(bounds, this->bounds());
62 }
63}
64
bsalomon47cc7692016-04-26 12:56:00 -070065int GrShape::unstyledKeySize() const {
66 if (fInheritedKey.count()) {
67 return fInheritedKey.count();
68 }
69 switch (fType) {
70 case Type::kEmpty:
71 return 1;
72 case Type::kRRect:
73 SkASSERT(!fInheritedKey.count());
74 SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
bsalomon70493962016-06-10 08:05:14 -070075 // + 1 for the direction, start index, and inverseness.
bsalomonee295642016-06-06 14:01:25 -070076 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
bsalomon47cc7692016-04-26 12:56:00 -070077 case Type::kPath:
78 if (fPath.get()->isVolatile()) {
79 return -1;
80 } else {
bsalomon70493962016-06-10 08:05:14 -070081 // The key is the path ID and fill type.
82 return 2;
bsalomon47cc7692016-04-26 12:56:00 -070083 }
84 }
85 SkFAIL("Should never get here.");
86 return 0;
87}
88
89void GrShape::writeUnstyledKey(uint32_t* key) const {
90 SkASSERT(this->unstyledKeySize());
91 SkDEBUGCODE(uint32_t* origKey = key;)
92 if (fInheritedKey.count()) {
93 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
94 SkDEBUGCODE(key += fInheritedKey.count();)
95 } else {
96 switch (fType) {
97 case Type::kEmpty:
98 *key++ = 1;
99 break;
100 case Type::kRRect:
101 fRRect.writeToMemory(key);
102 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
bsalomonee295642016-06-06 14:01:25 -0700103 *key = (fRRectDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
bsalomon70493962016-06-10 08:05:14 -0700104 *key |= fRRectIsInverted ? (1 << 30) : 0;
bsalomonee295642016-06-06 14:01:25 -0700105 *key++ |= fRRectStart;
106 SkASSERT(fRRectStart < 8);
bsalomon47cc7692016-04-26 12:56:00 -0700107 break;
108 case Type::kPath:
109 SkASSERT(!fPath.get()->isVolatile());
110 *key++ = fPath.get()->getGenerationID();
bsalomon70493962016-06-10 08:05:14 -0700111 // We could canonicalize the fill rule for paths that don't differentiate between
112 // even/odd or winding fill (e.g. convex).
113 *key++ = fPath.get()->getFillType();
bsalomon47cc7692016-04-26 12:56:00 -0700114 break;
115 }
116 }
117 SkASSERT(key - origKey == this->unstyledKeySize());
118}
119
bsalomon97fd2d42016-05-09 13:02:01 -0700120void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
bsalomon47cc7692016-04-26 12:56:00 -0700121 SkASSERT(!fInheritedKey.count());
122 // If the output shape turns out to be simple, then we will just use its geometric key
123 if (Type::kPath == fType) {
124 // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
125 // ApplyFullStyle(shape).
126 // The full key is structured as (geo,path_effect,stroke).
127 // If we do ApplyPathEffect we get get,path_effect as the inherited key. If we then
128 // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
129 // and then append the style key (which should now be stroke only) at the end.
130 int parentCnt = parent.fInheritedKey.count();
131 bool useParentGeoKey = !parentCnt;
132 if (useParentGeoKey) {
133 parentCnt = parent.unstyledKeySize();
bsalomon72dc51c2016-04-27 06:46:23 -0700134 if (parentCnt < 0) {
135 // The parent's geometry has no key so we will have no key.
136 fPath.get()->setIsVolatile(true);
137 return;
138 }
bsalomon47cc7692016-04-26 12:56:00 -0700139 }
bsalomon06077562016-05-04 13:50:29 -0700140 uint32_t styleKeyFlags = 0;
141 if (parent.knownToBeClosed()) {
142 styleKeyFlags |= GrStyle::kClosed_KeyFlag;
143 }
144 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
bsalomon47cc7692016-04-26 12:56:00 -0700145 if (styleCnt < 0) {
146 // The style doesn't allow a key, set the path to volatile so that we fail when
147 // we try to get a key for the shape.
148 fPath.get()->setIsVolatile(true);
bsalomon72dc51c2016-04-27 06:46:23 -0700149 return;
bsalomon47cc7692016-04-26 12:56:00 -0700150 }
bsalomon72dc51c2016-04-27 06:46:23 -0700151 fInheritedKey.reset(parentCnt + styleCnt);
152 if (useParentGeoKey) {
153 // This will be the geo key.
154 parent.writeUnstyledKey(fInheritedKey.get());
155 } else {
156 // This should be (geo,path_effect).
157 memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
158 parentCnt * sizeof(uint32_t));
159 }
160 // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
bsalomon97fd2d42016-05-09 13:02:01 -0700161 GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
162 styleKeyFlags);
bsalomon47cc7692016-04-26 12:56:00 -0700163 }
164}
165
166GrShape::GrShape(const GrShape& that) : fType(that.fType), fStyle(that.fStyle) {
167 switch (fType) {
168 case Type::kEmpty:
169 return;
170 case Type::kRRect:
171 fRRect = that.fRRect;
bsalomon70493962016-06-10 08:05:14 -0700172 fRRectDir = that.fRRectDir;
173 fRRectStart = that.fRRectStart;
174 fRRectIsInverted = that.fRRectIsInverted;
bsalomon47cc7692016-04-26 12:56:00 -0700175 return;
176 case Type::kPath:
177 fPath.set(*that.fPath.get());
178 return;
179 }
180 fInheritedKey.reset(that.fInheritedKey.count());
181 memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
182 sizeof(uint32_t) * fInheritedKey.count());
183}
184
bsalomon97fd2d42016-05-09 13:02:01 -0700185GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
186 // TODO: Add some quantization of scale for better cache performance here or leave that up
187 // to caller?
188 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
189 // stroke of a rect).
bsalomonfb083272016-05-04 08:27:41 -0700190 if (!parent.style().applies() ||
191 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
192 fType = Type::kEmpty;
193 *this = parent;
194 return;
195 }
196
bsalomon47cc7692016-04-26 12:56:00 -0700197 SkPathEffect* pe = parent.fStyle.pathEffect();
bsalomonfb083272016-05-04 08:27:41 -0700198 SkTLazy<SkPath> tmpPath;
199 const GrShape* parentForKey = &parent;
200 SkTLazy<GrShape> tmpParent;
201 fType = Type::kPath;
202 fPath.init();
bsalomon47cc7692016-04-26 12:56:00 -0700203 if (pe) {
bsalomonfb083272016-05-04 08:27:41 -0700204 SkPath* srcForPathEffect;
bsalomon47cc7692016-04-26 12:56:00 -0700205 if (parent.fType == Type::kPath) {
bsalomonfb083272016-05-04 08:27:41 -0700206 srcForPathEffect = parent.fPath.get();
bsalomon47cc7692016-04-26 12:56:00 -0700207 } else {
bsalomonfb083272016-05-04 08:27:41 -0700208 srcForPathEffect = tmpPath.init();
209 parent.asPath(tmpPath.get());
bsalomon47cc7692016-04-26 12:56:00 -0700210 }
211 // Should we consider bounds? Would have to include in key, but it'd be nice to know
212 // if the bounds actually modified anything before including in key.
bsalomonfb083272016-05-04 08:27:41 -0700213 SkStrokeRec strokeRec = parent.fStyle.strokeRec();
bsalomon97fd2d42016-05-09 13:02:01 -0700214 strokeRec.setResScale(scale);
bsalomonfb083272016-05-04 08:27:41 -0700215 if (!pe->filterPath(fPath.get(), *srcForPathEffect, &strokeRec, nullptr)) {
bsalomond6723842016-06-07 12:20:15 -0700216 // If the path effect fails then we continue as though there was no path effect.
217 // If the original was a rrect that we couldn't canonicalize because of the path
218 // effect, then do so now.
219 if (parent.fType == Type::kRRect && (parent.fRRectDir != kDefaultRRectDir ||
220 parent.fRRectStart != kDefaultRRectStart)) {
221 SkASSERT(srcForPathEffect == tmpPath.get());
222 tmpPath.get()->reset();
223 tmpPath.get()->addRRect(parent.fRRect, kDefaultRRectDir, kDefaultRRectDir);
224 }
225 *fPath.get() = *srcForPathEffect;
bsalomon47cc7692016-04-26 12:56:00 -0700226 }
bsalomon97fd2d42016-05-09 13:02:01 -0700227 // A path effect has access to change the res scale but we aren't expecting it to and it
228 // would mess up our key computation.
229 SkASSERT(scale == strokeRec.getResScale());
bsalomonfb083272016-05-04 08:27:41 -0700230 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply) {
231 if (strokeRec.needToApply()) {
232 // The intermediate shape may not be a general path. If we we're just applying
233 // the path effect then attemptToReduceFromPath would catch it. This means that
234 // when we subsequently applied the remaining strokeRec we would have a non-path
235 // parent shape that would be used to determine the the stroked path's key.
236 // We detect that case here and change parentForKey to a temporary that represents
237 // the simpler shape so that applying both path effect and the strokerec all at
238 // once produces the same key.
239 SkRRect rrect;
bsalomonee295642016-06-06 14:01:25 -0700240 SkPath::Direction dir;
241 unsigned start;
bsalomon70493962016-06-10 08:05:14 -0700242 bool inverted;
bsalomonee295642016-06-06 14:01:25 -0700243 Type parentType = AttemptToReduceFromPathImpl(*fPath.get(), &rrect, &dir, &start,
bsalomon70493962016-06-10 08:05:14 -0700244 &inverted, nullptr, strokeRec);
bsalomonfb083272016-05-04 08:27:41 -0700245 switch (parentType) {
246 case Type::kEmpty:
247 tmpParent.init();
248 parentForKey = tmpParent.get();
249 break;
250 case Type::kRRect:
bsalomon70493962016-06-10 08:05:14 -0700251 tmpParent.init(rrect, dir, start, inverted, GrStyle(strokeRec, nullptr));
bsalomonfb083272016-05-04 08:27:41 -0700252 parentForKey = tmpParent.get();
253 case Type::kPath:
254 break;
255 }
256 SkAssertResult(strokeRec.applyToPath(fPath.get(), *fPath.get()));
257 } else {
258 fStyle = GrStyle(strokeRec, nullptr);
bsalomon72dc51c2016-04-27 06:46:23 -0700259 }
bsalomonfb083272016-05-04 08:27:41 -0700260 } else {
261 fStyle = GrStyle(strokeRec, nullptr);
bsalomon72dc51c2016-04-27 06:46:23 -0700262 }
bsalomon47cc7692016-04-26 12:56:00 -0700263 } else {
bsalomon97fd2d42016-05-09 13:02:01 -0700264 const SkPath* srcForParentStyle;
bsalomonfb083272016-05-04 08:27:41 -0700265 if (parent.fType == Type::kPath) {
bsalomon97fd2d42016-05-09 13:02:01 -0700266 srcForParentStyle = parent.fPath.get();
bsalomonfb083272016-05-04 08:27:41 -0700267 } else {
bsalomon97fd2d42016-05-09 13:02:01 -0700268 srcForParentStyle = tmpPath.init();
bsalomonfb083272016-05-04 08:27:41 -0700269 parent.asPath(tmpPath.get());
270 }
bsalomon97fd2d42016-05-09 13:02:01 -0700271 SkStrokeRec::InitStyle fillOrHairline;
272 SkASSERT(parent.fStyle.applies());
273 SkASSERT(!parent.fStyle.pathEffect());
274 SkAssertResult(parent.fStyle.applyToPath(fPath.get(), &fillOrHairline, *srcForParentStyle,
275 scale));
276 fStyle.resetToInitStyle(fillOrHairline);
bsalomon47cc7692016-04-26 12:56:00 -0700277 }
bsalomon72dc51c2016-04-27 06:46:23 -0700278 this->attemptToReduceFromPath();
bsalomon97fd2d42016-05-09 13:02:01 -0700279 this->setInheritedKey(*parentForKey, apply, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700280}
bsalomonee295642016-06-06 14:01:25 -0700281
bsalomon70493962016-06-10 08:05:14 -0700282static inline bool rrect_path_is_inverse_filled(const SkPath& path, const SkStrokeRec& strokeRec,
283 const SkPathEffect* pe) {
284 // Dashing doesn't use the path fill type. Dashing only works with stroking
285 if (pe && pe->asADash(nullptr)) {
286 pe = nullptr;
287 }
288
289 SkStrokeRec::Style style = strokeRec.getStyle();
290 if (!pe && (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style)) {
291 // stroking ignores the path fill rule.
292 return false;
293 }
294 return path.isInverseFillType();
295}
296
bsalomonee295642016-06-06 14:01:25 -0700297GrShape::Type GrShape::AttemptToReduceFromPathImpl(const SkPath& path, SkRRect* rrect,
298 SkPath::Direction* rrectDir,
299 unsigned* rrectStart,
bsalomon70493962016-06-10 08:05:14 -0700300 bool* rrectIsInverted,
bsalomonee295642016-06-06 14:01:25 -0700301 const SkPathEffect* pe,
302 const SkStrokeRec& strokeRec) {
303 if (path.isEmpty()) {
304 return Type::kEmpty;
305 }
306 if (path.isRRect(rrect, rrectDir, rrectStart)) {
307 // Currently SkPath does not acknowledge that empty, rect, or oval subtypes as rrects.
308 SkASSERT(!rrect->isEmpty());
309 SkASSERT(rrect->getType() != SkRRect::kRect_Type);
310 SkASSERT(rrect->getType() != SkRRect::kOval_Type);
311 if (!pe) {
312 *rrectStart = DefaultRRectDirAndStartIndex(*rrect, false, rrectDir);
313 }
bsalomon70493962016-06-10 08:05:14 -0700314 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe);
bsalomonee295642016-06-06 14:01:25 -0700315 return Type::kRRect;
316 }
317 SkRect rect;
318 if (path.isOval(&rect, rrectDir, rrectStart)) {
319 rrect->setOval(rect);
320 if (!pe) {
321 *rrectDir = kDefaultRRectDir;
322 *rrectStart = kDefaultRRectStart;
323 } else {
324 // convert from oval indexing to rrect indexiing.
325 *rrectStart *= 2;
326 }
bsalomon70493962016-06-10 08:05:14 -0700327 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe);
bsalomonee295642016-06-06 14:01:25 -0700328 return Type::kRRect;
329 }
330 // When there is a path effect we restrict rect detection to the narrower API that
331 // gives us the starting position. Otherwise, we will retry with the more aggressive isRect().
332 if (SkPathPriv::IsSimpleClosedRect(path, &rect, rrectDir, rrectStart)) {
333 if (!pe) {
334 *rrectDir = kDefaultRRectDir;
335 *rrectStart = kDefaultRRectStart;
336 } else {
337 // convert from rect indexing to rrect indexiing.
338 *rrectStart *= 2;
339 }
340 rrect->setRect(rect);
bsalomon70493962016-06-10 08:05:14 -0700341 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe);
bsalomonee295642016-06-06 14:01:25 -0700342 return Type::kRRect;
343 }
344 if (!pe) {
345 bool closed;
346 if (path.isRect(&rect, &closed, nullptr)) {
347 if (closed || strokeRec.isFillStyle()) {
348 rrect->setRect(rect);
349 // Since there is no path effect the dir and start index is immaterial.
350 *rrectDir = kDefaultRRectDir;
351 *rrectStart = kDefaultRRectStart;
bsalomon70493962016-06-10 08:05:14 -0700352 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe);
bsalomonee295642016-06-06 14:01:25 -0700353 return Type::kRRect;
354 }
355 }
356 }
357 return Type::kPath;
358}