OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrShape.h" | 8 #include "GrShape.h" |
9 | 9 |
10 GrShape& GrShape::operator=(const GrShape& that) { | 10 GrShape& GrShape::operator=(const GrShape& that) { |
11 bool wasPath = Type::kPath == fType; | 11 bool wasPath = Type::kPath == fType; |
12 fStyle = that.fStyle; | 12 fStyle = that.fStyle; |
13 fType = that.fType; | 13 fType = that.fType; |
14 switch (fType) { | 14 switch (fType) { |
15 case Type::kEmpty: | 15 case Type::kEmpty: |
16 if (wasPath) { | 16 if (wasPath) { |
17 fPath.reset(); | 17 fPath.reset(); |
18 } | 18 } |
19 break; | 19 break; |
20 case Type::kRRect: | 20 case Type::kRRect: |
21 if (wasPath) { | 21 if (wasPath) { |
22 fPath.reset(); | 22 fPath.reset(); |
23 } | 23 } |
24 fRRect = that.fRRect; | 24 fRRect = that.fRRect; |
25 fRRectDir = that.fRRectDir; | 25 fRRectDir = that.fRRectDir; |
26 fRRectStart = that.fRRectStart; | 26 fRRectStart = that.fRRectStart; |
| 27 fRRectIsInverted = that.fRRectIsInverted; |
27 break; | 28 break; |
28 case Type::kPath: | 29 case Type::kPath: |
29 if (wasPath) { | 30 if (wasPath) { |
30 *fPath.get() = *that.fPath.get(); | 31 *fPath.get() = *that.fPath.get(); |
31 } else { | 32 } else { |
32 fPath.set(*that.fPath.get()); | 33 fPath.set(*that.fPath.get()); |
33 } | 34 } |
34 break; | 35 break; |
35 } | 36 } |
36 fInheritedKey.reset(that.fInheritedKey.count()); | 37 fInheritedKey.reset(that.fInheritedKey.count()); |
(...skipping 27 matching lines...) Expand all Loading... |
64 int GrShape::unstyledKeySize() const { | 65 int GrShape::unstyledKeySize() const { |
65 if (fInheritedKey.count()) { | 66 if (fInheritedKey.count()) { |
66 return fInheritedKey.count(); | 67 return fInheritedKey.count(); |
67 } | 68 } |
68 switch (fType) { | 69 switch (fType) { |
69 case Type::kEmpty: | 70 case Type::kEmpty: |
70 return 1; | 71 return 1; |
71 case Type::kRRect: | 72 case Type::kRRect: |
72 SkASSERT(!fInheritedKey.count()); | 73 SkASSERT(!fInheritedKey.count()); |
73 SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); | 74 SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t)); |
74 // + 1 for the direction + start index. | 75 // + 1 for the direction, start index, and inverseness. |
75 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1; | 76 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1; |
76 case Type::kPath: | 77 case Type::kPath: |
77 if (fPath.get()->isVolatile()) { | 78 if (fPath.get()->isVolatile()) { |
78 return -1; | 79 return -1; |
79 } else { | 80 } else { |
80 return 1; | 81 // The key is the path ID and fill type. |
| 82 return 2; |
81 } | 83 } |
82 } | 84 } |
83 SkFAIL("Should never get here."); | 85 SkFAIL("Should never get here."); |
84 return 0; | 86 return 0; |
85 } | 87 } |
86 | 88 |
87 void GrShape::writeUnstyledKey(uint32_t* key) const { | 89 void GrShape::writeUnstyledKey(uint32_t* key) const { |
88 SkASSERT(this->unstyledKeySize()); | 90 SkASSERT(this->unstyledKeySize()); |
89 SkDEBUGCODE(uint32_t* origKey = key;) | 91 SkDEBUGCODE(uint32_t* origKey = key;) |
90 if (fInheritedKey.count()) { | 92 if (fInheritedKey.count()) { |
91 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count(
)); | 93 memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count(
)); |
92 SkDEBUGCODE(key += fInheritedKey.count();) | 94 SkDEBUGCODE(key += fInheritedKey.count();) |
93 } else { | 95 } else { |
94 switch (fType) { | 96 switch (fType) { |
95 case Type::kEmpty: | 97 case Type::kEmpty: |
96 *key++ = 1; | 98 *key++ = 1; |
97 break; | 99 break; |
98 case Type::kRRect: | 100 case Type::kRRect: |
99 fRRect.writeToMemory(key); | 101 fRRect.writeToMemory(key); |
100 key += SkRRect::kSizeInMemory / sizeof(uint32_t); | 102 key += SkRRect::kSizeInMemory / sizeof(uint32_t); |
101 *key = (fRRectDir == SkPath::kCCW_Direction) ? (1 << 31) : 0; | 103 *key = (fRRectDir == SkPath::kCCW_Direction) ? (1 << 31) : 0; |
| 104 *key |= fRRectIsInverted ? (1 << 30) : 0; |
102 *key++ |= fRRectStart; | 105 *key++ |= fRRectStart; |
103 SkASSERT(fRRectStart < 8); | 106 SkASSERT(fRRectStart < 8); |
104 break; | 107 break; |
105 case Type::kPath: | 108 case Type::kPath: |
106 SkASSERT(!fPath.get()->isVolatile()); | 109 SkASSERT(!fPath.get()->isVolatile()); |
107 *key++ = fPath.get()->getGenerationID(); | 110 *key++ = fPath.get()->getGenerationID(); |
| 111 // We could canonicalize the fill rule for paths that don't diff
erentiate between |
| 112 // even/odd or winding fill (e.g. convex). |
| 113 *key++ = fPath.get()->getFillType(); |
108 break; | 114 break; |
109 } | 115 } |
110 } | 116 } |
111 SkASSERT(key - origKey == this->unstyledKeySize()); | 117 SkASSERT(key - origKey == this->unstyledKeySize()); |
112 } | 118 } |
113 | 119 |
114 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkSca
lar scale) { | 120 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkSca
lar scale) { |
115 SkASSERT(!fInheritedKey.count()); | 121 SkASSERT(!fInheritedKey.count()); |
116 // If the output shape turns out to be simple, then we will just use its geo
metric key | 122 // If the output shape turns out to be simple, then we will just use its geo
metric key |
117 if (Type::kPath == fType) { | 123 if (Type::kPath == fType) { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 styleKeyFlags); | 162 styleKeyFlags); |
157 } | 163 } |
158 } | 164 } |
159 | 165 |
160 GrShape::GrShape(const GrShape& that) : fType(that.fType), fStyle(that.fStyle) { | 166 GrShape::GrShape(const GrShape& that) : fType(that.fType), fStyle(that.fStyle) { |
161 switch (fType) { | 167 switch (fType) { |
162 case Type::kEmpty: | 168 case Type::kEmpty: |
163 return; | 169 return; |
164 case Type::kRRect: | 170 case Type::kRRect: |
165 fRRect = that.fRRect; | 171 fRRect = that.fRRect; |
| 172 fRRectDir = that.fRRectDir; |
| 173 fRRectStart = that.fRRectStart; |
| 174 fRRectIsInverted = that.fRRectIsInverted; |
166 return; | 175 return; |
167 case Type::kPath: | 176 case Type::kPath: |
168 fPath.set(*that.fPath.get()); | 177 fPath.set(*that.fPath.get()); |
169 return; | 178 return; |
170 } | 179 } |
171 fInheritedKey.reset(that.fInheritedKey.count()); | 180 fInheritedKey.reset(that.fInheritedKey.count()); |
172 memcpy(fInheritedKey.get(), that.fInheritedKey.get(), | 181 memcpy(fInheritedKey.get(), that.fInheritedKey.get(), |
173 sizeof(uint32_t) * fInheritedKey.count()); | 182 sizeof(uint32_t) * fInheritedKey.count()); |
174 } | 183 } |
175 | 184 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 // The intermediate shape may not be a general path. If we we're
just applying | 232 // The intermediate shape may not be a general path. If we we're
just applying |
224 // the path effect then attemptToReduceFromPath would catch it.
This means that | 233 // the path effect then attemptToReduceFromPath would catch it.
This means that |
225 // when we subsequently applied the remaining strokeRec we would
have a non-path | 234 // when we subsequently applied the remaining strokeRec we would
have a non-path |
226 // parent shape that would be used to determine the the stroked
path's key. | 235 // parent shape that would be used to determine the the stroked
path's key. |
227 // We detect that case here and change parentForKey to a tempora
ry that represents | 236 // We detect that case here and change parentForKey to a tempora
ry that represents |
228 // the simpler shape so that applying both path effect and the s
trokerec all at | 237 // the simpler shape so that applying both path effect and the s
trokerec all at |
229 // once produces the same key. | 238 // once produces the same key. |
230 SkRRect rrect; | 239 SkRRect rrect; |
231 SkPath::Direction dir; | 240 SkPath::Direction dir; |
232 unsigned start; | 241 unsigned start; |
| 242 bool inverted; |
233 Type parentType = AttemptToReduceFromPathImpl(*fPath.get(), &rre
ct, &dir, &start, | 243 Type parentType = AttemptToReduceFromPathImpl(*fPath.get(), &rre
ct, &dir, &start, |
234 nullptr, strokeRec
); | 244 &inverted, nullptr
, strokeRec); |
235 switch (parentType) { | 245 switch (parentType) { |
236 case Type::kEmpty: | 246 case Type::kEmpty: |
237 tmpParent.init(); | 247 tmpParent.init(); |
238 parentForKey = tmpParent.get(); | 248 parentForKey = tmpParent.get(); |
239 break; | 249 break; |
240 case Type::kRRect: | 250 case Type::kRRect: |
241 tmpParent.init(rrect, dir, start, GrStyle(strokeRec, nul
lptr)); | 251 tmpParent.init(rrect, dir, start, inverted, GrStyle(stro
keRec, nullptr)); |
242 parentForKey = tmpParent.get(); | 252 parentForKey = tmpParent.get(); |
243 case Type::kPath: | 253 case Type::kPath: |
244 break; | 254 break; |
245 } | 255 } |
246 SkAssertResult(strokeRec.applyToPath(fPath.get(), *fPath.get()))
; | 256 SkAssertResult(strokeRec.applyToPath(fPath.get(), *fPath.get()))
; |
247 } else { | 257 } else { |
248 fStyle = GrStyle(strokeRec, nullptr); | 258 fStyle = GrStyle(strokeRec, nullptr); |
249 } | 259 } |
250 } else { | 260 } else { |
251 fStyle = GrStyle(strokeRec, nullptr); | 261 fStyle = GrStyle(strokeRec, nullptr); |
(...skipping 10 matching lines...) Expand all Loading... |
262 SkASSERT(parent.fStyle.applies()); | 272 SkASSERT(parent.fStyle.applies()); |
263 SkASSERT(!parent.fStyle.pathEffect()); | 273 SkASSERT(!parent.fStyle.pathEffect()); |
264 SkAssertResult(parent.fStyle.applyToPath(fPath.get(), &fillOrHairline, *
srcForParentStyle, | 274 SkAssertResult(parent.fStyle.applyToPath(fPath.get(), &fillOrHairline, *
srcForParentStyle, |
265 scale)); | 275 scale)); |
266 fStyle.resetToInitStyle(fillOrHairline); | 276 fStyle.resetToInitStyle(fillOrHairline); |
267 } | 277 } |
268 this->attemptToReduceFromPath(); | 278 this->attemptToReduceFromPath(); |
269 this->setInheritedKey(*parentForKey, apply, scale); | 279 this->setInheritedKey(*parentForKey, apply, scale); |
270 } | 280 } |
271 | 281 |
| 282 static inline bool rrect_path_is_inverse_filled(const SkPath& path, const SkStro
keRec& 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_St
yle == style)) { |
| 291 // stroking ignores the path fill rule. |
| 292 return false; |
| 293 } |
| 294 return path.isInverseFillType(); |
| 295 } |
| 296 |
272 GrShape::Type GrShape::AttemptToReduceFromPathImpl(const SkPath& path, SkRRect*
rrect, | 297 GrShape::Type GrShape::AttemptToReduceFromPathImpl(const SkPath& path, SkRRect*
rrect, |
273 SkPath::Direction* rrectDir, | 298 SkPath::Direction* rrectDir, |
274 unsigned* rrectStart, | 299 unsigned* rrectStart, |
| 300 bool* rrectIsInverted, |
275 const SkPathEffect* pe, | 301 const SkPathEffect* pe, |
276 const SkStrokeRec& strokeRec)
{ | 302 const SkStrokeRec& strokeRec)
{ |
277 if (path.isEmpty()) { | 303 if (path.isEmpty()) { |
278 return Type::kEmpty; | 304 return Type::kEmpty; |
279 } | 305 } |
280 if (path.isRRect(rrect, rrectDir, rrectStart)) { | 306 if (path.isRRect(rrect, rrectDir, rrectStart)) { |
281 // Currently SkPath does not acknowledge that empty, rect, or oval subty
pes as rrects. | 307 // Currently SkPath does not acknowledge that empty, rect, or oval subty
pes as rrects. |
282 SkASSERT(!rrect->isEmpty()); | 308 SkASSERT(!rrect->isEmpty()); |
283 SkASSERT(rrect->getType() != SkRRect::kRect_Type); | 309 SkASSERT(rrect->getType() != SkRRect::kRect_Type); |
284 SkASSERT(rrect->getType() != SkRRect::kOval_Type); | 310 SkASSERT(rrect->getType() != SkRRect::kOval_Type); |
285 if (!pe) { | 311 if (!pe) { |
286 *rrectStart = DefaultRRectDirAndStartIndex(*rrect, false, rrectDir); | 312 *rrectStart = DefaultRRectDirAndStartIndex(*rrect, false, rrectDir); |
287 } | 313 } |
| 314 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe); |
288 return Type::kRRect; | 315 return Type::kRRect; |
289 } | 316 } |
290 SkRect rect; | 317 SkRect rect; |
291 if (path.isOval(&rect, rrectDir, rrectStart)) { | 318 if (path.isOval(&rect, rrectDir, rrectStart)) { |
292 rrect->setOval(rect); | 319 rrect->setOval(rect); |
293 if (!pe) { | 320 if (!pe) { |
294 *rrectDir = kDefaultRRectDir; | 321 *rrectDir = kDefaultRRectDir; |
295 *rrectStart = kDefaultRRectStart; | 322 *rrectStart = kDefaultRRectStart; |
296 } else { | 323 } else { |
297 // convert from oval indexing to rrect indexiing. | 324 // convert from oval indexing to rrect indexiing. |
298 *rrectStart *= 2; | 325 *rrectStart *= 2; |
299 } | 326 } |
| 327 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe); |
300 return Type::kRRect; | 328 return Type::kRRect; |
301 } | 329 } |
302 // When there is a path effect we restrict rect detection to the narrower AP
I that | 330 // When there is a path effect we restrict rect detection to the narrower AP
I that |
303 // gives us the starting position. Otherwise, we will retry with the more ag
gressive isRect(). | 331 // gives us the starting position. Otherwise, we will retry with the more ag
gressive isRect(). |
304 if (SkPathPriv::IsSimpleClosedRect(path, &rect, rrectDir, rrectStart)) { | 332 if (SkPathPriv::IsSimpleClosedRect(path, &rect, rrectDir, rrectStart)) { |
305 if (!pe) { | 333 if (!pe) { |
306 *rrectDir = kDefaultRRectDir; | 334 *rrectDir = kDefaultRRectDir; |
307 *rrectStart = kDefaultRRectStart; | 335 *rrectStart = kDefaultRRectStart; |
308 } else { | 336 } else { |
309 // convert from rect indexing to rrect indexiing. | 337 // convert from rect indexing to rrect indexiing. |
310 *rrectStart *= 2; | 338 *rrectStart *= 2; |
311 } | 339 } |
312 rrect->setRect(rect); | 340 rrect->setRect(rect); |
| 341 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec, pe); |
313 return Type::kRRect; | 342 return Type::kRRect; |
314 } | 343 } |
315 if (!pe) { | 344 if (!pe) { |
316 bool closed; | 345 bool closed; |
317 if (path.isRect(&rect, &closed, nullptr)) { | 346 if (path.isRect(&rect, &closed, nullptr)) { |
318 if (closed || strokeRec.isFillStyle()) { | 347 if (closed || strokeRec.isFillStyle()) { |
319 rrect->setRect(rect); | 348 rrect->setRect(rect); |
320 // Since there is no path effect the dir and start index is imma
terial. | 349 // Since there is no path effect the dir and start index is imma
terial. |
321 *rrectDir = kDefaultRRectDir; | 350 *rrectDir = kDefaultRRectDir; |
322 *rrectStart = kDefaultRRectStart; | 351 *rrectStart = kDefaultRRectStart; |
| 352 *rrectIsInverted = rrect_path_is_inverse_filled(path, strokeRec,
pe); |
323 return Type::kRRect; | 353 return Type::kRRect; |
324 } | 354 } |
325 } | 355 } |
326 } | 356 } |
327 return Type::kPath; | 357 return Type::kPath; |
328 } | 358 } |
OLD | NEW |