1 /*
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
operator =(const GrShape & that)10 GrShape& GrShape::operator=(const GrShape& that) {
11 fStyle = that.fStyle;
12 this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
13 switch (fType) {
14 case Type::kEmpty:
15 break;
16 case Type::kInvertedEmpty:
17 break;
18 case Type::kRRect:
19 fRRectData = that.fRRectData;
20 break;
21 case Type::kLine:
22 fLineData = that.fLineData;
23 break;
24 case Type::kPath:
25 fPathData.fGenID = that.fPathData.fGenID;
26 break;
27 }
28 fInheritedKey.reset(that.fInheritedKey.count());
29 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
30 sizeof(uint32_t) * fInheritedKey.count());
31 if (that.fInheritedPathForListeners.isValid()) {
32 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
33 } else {
34 fInheritedPathForListeners.reset();
35 }
36 return *this;
37 }
38
flip_inversion(bool originalIsInverted,GrShape::FillInversion inversion)39 static 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
is_inverted(bool originalIsInverted,GrShape::FillInversion inversion)53 static 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
MakeFilled(const GrShape & original,FillInversion inversion)67 GrShape 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;
74 if (original.fInheritedPathForListeners.isValid()) {
75 result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get());
76 }
77 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
bounds() const118 SkRect GrShape::bounds() const {
119 // 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);
122 switch (fType) {
123 case Type::kEmpty:
124 return kInverted;
125 case Type::kInvertedEmpty:
126 return kInverted;
127 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 }
145 case Type::kRRect:
146 return fRRectData.fRRect.getBounds();
147 case Type::kPath:
148 return this->path().getBounds();
149 }
150 SK_ABORT("Unknown shape type");
151 return kInverted;
152 }
153
styledBounds() const154 SkRect GrShape::styledBounds() const {
155 if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
156 return SkRect::MakeEmpty();
157 }
158
159 SkRect bounds;
160 fStyle.adjustBounds(&bounds, this->bounds());
161 return bounds;
162 }
163
164 // If the path is small enough to be keyed from its data this returns key length, otherwise -1.
path_key_from_data_size(const SkPath & path)165 static 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.
write_path_key_from_data(const SkPath & path,uint32_t * origKey)181 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
182 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;
201 sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
202 GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
203 SkDEBUGCODE(key += conicWeightCnt);
204 SkASSERT(key - origKey == path_key_from_data_size(path));
205 }
206
unstyledKeySize() const207 int GrShape::unstyledKeySize() const {
208 if (fInheritedKey.count()) {
209 return fInheritedKey.count();
210 }
211 switch (fType) {
212 case Type::kEmpty:
213 return 1;
214 case Type::kInvertedEmpty:
215 return 1;
216 case Type::kRRect:
217 SkASSERT(!fInheritedKey.count());
218 SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
219 // + 1 for the direction, start index, and inverseness.
220 return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
221 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;
225 case Type::kPath: {
226 if (0 == fPathData.fGenID) {
227 return -1;
228 }
229 int dataKeySize = path_key_from_data_size(fPathData.fPath);
230 if (dataKeySize >= 0) {
231 return dataKeySize;
232 }
233 // The key is the path ID and fill type.
234 return 2;
235 }
236 }
237 SK_ABORT("Should never get here.");
238 return 0;
239 }
240
writeUnstyledKey(uint32_t * key) const241 void 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;
252 case Type::kInvertedEmpty:
253 *key++ = 2;
254 break;
255 case Type::kRRect:
256 fRRectData.fRRect.writeToMemory(key);
257 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
258 *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);
262 break;
263 case Type::kLine:
264 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
265 key += 4;
266 *key++ = fLineData.fInverted ? 1 : 0;
267 break;
268 case Type::kPath: {
269 SkASSERT(fPathData.fGenID);
270 int dataKeySize = path_key_from_data_size(fPathData.fPath);
271 if (dataKeySize >= 0) {
272 write_path_key_from_data(fPathData.fPath, key);
273 return;
274 }
275 *key++ = fPathData.fGenID;
276 // We could canonicalize the fill rule for paths that don't differentiate between
277 // even/odd or winding fill (e.g. convex).
278 *key++ = this->path().getFillType();
279 break;
280 }
281 }
282 }
283 SkASSERT(key - origKey == this->unstyledKeySize());
284 }
285
setInheritedKey(const GrShape & parent,GrStyle::Apply apply,SkScalar scale)286 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
287 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).
293 // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
294 // 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();
300 if (parentCnt < 0) {
301 // The parent's geometry has no key so we will have no key.
302 fPathData.fGenID = 0;
303 return;
304 }
305 }
306 uint32_t styleKeyFlags = 0;
307 if (parent.knownToBeClosed()) {
308 styleKeyFlags |= GrStyle::kClosed_KeyFlag;
309 }
310 if (parent.asLine(nullptr, nullptr)) {
311 styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
312 }
313 int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
314 if (styleCnt < 0) {
315 // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
316 // we try to get a key for the shape.
317 fPathData.fGenID = 0;
318 return;
319 }
320 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)
330 GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
331 styleKeyFlags);
332 }
333 }
334
originalPathForListeners() const335 const 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;
342 }
343
addGenIDChangeListener(SkPathRef::GenIDChangeListener * listener) const344 void 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
GrShape(const GrShape & that)352 GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
353 const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
354 this->initType(that.fType, thatPath);
355 switch (fType) {
356 case Type::kEmpty:
357 break;
358 case Type::kInvertedEmpty:
359 break;
360 case Type::kRRect:
361 fRRectData = that.fRRectData;
362 break;
363 case Type::kLine:
364 fLineData = that.fLineData;
365 break;
366 case Type::kPath:
367 fPathData.fGenID = that.fPathData.fGenID;
368 break;
369 }
370 fInheritedKey.reset(that.fInheritedKey.count());
371 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
372 sizeof(uint32_t) * fInheritedKey.count());
373 if (that.fInheritedPathForListeners.isValid()) {
374 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
375 }
376 }
377
GrShape(const GrShape & parent,GrStyle::Apply apply,SkScalar scale)378 GrShape::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).
383 if (!parent.style().applies() ||
384 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
385 this->initType(Type::kEmpty);
386 *this = parent;
387 return;
388 }
389
390 SkPathEffect* pe = parent.fStyle.pathEffect();
391 SkTLazy<SkPath> tmpPath;
392 const GrShape* parentForKey = &parent;
393 SkTLazy<GrShape> tmpParent;
394 this->initType(Type::kPath);
395 fPathData.fGenID = 0;
396 if (pe) {
397 const SkPath* srcForPathEffect;
398 if (parent.fType == Type::kPath) {
399 srcForPathEffect = &parent.path();
400 } else {
401 srcForPathEffect = tmpPath.init();
402 parent.asPath(tmpPath.get());
403 }
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.
406 SkStrokeRec strokeRec = parent.fStyle.strokeRec();
407 if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
408 scale)) {
409 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
410 *this = tmpParent.get()->applyStyle(apply, scale);
411 return;
412 }
413 // 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());
416 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.
424 tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
425 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
426 if (!tmpPath.isValid()) {
427 tmpPath.init();
428 }
429 tmpParent.get()->asPath(tmpPath.get());
430 SkStrokeRec::InitStyle fillOrHairline;
431 // 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 }
441 fStyle.resetToInitStyle(fillOrHairline);
442 parentForKey = tmpParent.get();
443 } else {
444 fStyle = GrStyle(strokeRec, nullptr);
445 }
446 } else {
447 const SkPath* srcForParentStyle;
448 if (parent.fType == Type::kPath) {
449 srcForParentStyle = &parent.path();
450 } else {
451 srcForParentStyle = tmpPath.init();
452 parent.asPath(tmpPath.get());
453 }
454 SkStrokeRec::InitStyle fillOrHairline;
455 SkASSERT(parent.fStyle.applies());
456 SkASSERT(!parent.fStyle.pathEffect());
457 SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
458 scale));
459 fStyle.resetToInitStyle(fillOrHairline);
460 }
461 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 }
466 this->attemptToSimplifyPath();
467 this->setInheritedKey(*parentForKey, apply, scale);
468 }
469
attemptToSimplifyPath()470 void GrShape::attemptToSimplifyPath() {
471 SkRect rect;
472 SkRRect rrect;
473 SkPath::Direction rrectDir;
474 unsigned rrectStart;
475 bool inverted = this->path().isInverseFillType();
476 SkPoint pts[2];
477 if (this->path().isEmpty()) {
478 // Dashing ignores inverseness skbug.com/5421.
479 this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty
480 : Type::kEmpty);
481 } 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;
486 } 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;
492 SkASSERT(!fRRectData.fRRect.isEmpty());
493 } 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;
498 // convert from oval indexing to rrect indexiing.
499 fRRectData.fStart = 2 * rrectStart;
500 } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
501 this->changeType(Type::kRRect);
502 // 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().
505 fRRectData.fRRect.setRect(rect);
506 fRRectData.fInverted = inverted;
507 fRRectData.fDir = rrectDir;
508 // convert from rect indexing to rrect indexiing.
509 fRRectData.fStart = 2 * rrectStart;
510 } else if (!this->style().hasPathEffect()) {
511 bool closed;
512 if (this->path().isRect(&rect, &closed, nullptr)) {
513 if (closed || this->style().isSimpleFill()) {
514 this->changeType(Type::kRRect);
515 fRRectData.fRRect.setRect(rect);
516 // Since there is no path effect the dir and start index is immaterial.
517 fRRectData.fDir = kDefaultRRectDir;
518 fRRectData.fStart = kDefaultRRectStart;
519 // There isn't dashing so we will have to preserver inverseness.
520 fRRectData.fInverted = inverted;
521 }
522 }
523 }
524 if (Type::kPath != fType) {
525 fInheritedKey.reset(0);
526 // 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.
529 fInheritedPathForListeners.reset();
530 if (Type::kRRect == fType) {
531 this->attemptToSimplifyRRect();
532 } else if (Type::kLine == fType) {
533 this->attemptToSimplifyLine();
534 }
535 } else {
536 if (fInheritedKey.count() || this->path().isVolatile()) {
537 fPathData.fGenID = 0;
538 } else {
539 fPathData.fGenID = this->path().getGenerationID();
540 }
541 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)
546 bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
547 if (inverse) {
548 this->path().setFillType(kDefaultPathInverseFillType);
549 } else {
550 this->path().setFillType(kDefaultPathFillType);
551 }
552 } else if (this->path().isConvex()) {
553 // There is no distinction between even/odd and non-zero winding count for convex
554 // paths.
555 if (this->path().isInverseFillType()) {
556 this->path().setFillType(kDefaultPathInverseFillType);
557 } else {
558 this->path().setFillType(kDefaultPathFillType);
559 }
560 }
561 }
562 }
563 }
564
attemptToSimplifyRRect()565 void GrShape::attemptToSimplifyRRect() {
566 SkASSERT(Type::kRRect == fType);
567 SkASSERT(!fInheritedKey.count());
568 if (fRRectData.fRRect.isEmpty()) {
569 // 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 }
584 }
585 if (!this->style().hasPathEffect()) {
586 fRRectData.fDir = kDefaultRRectDir;
587 fRRectData.fStart = kDefaultRRectStart;
588 } else if (fStyle.isDashed()) {
589 // Dashing ignores the inverseness (currently). skbug.com/5421
590 fRRectData.fInverted = false;
591 }
592 // 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 }
602 }
603
attemptToSimplifyLine()604 void GrShape::attemptToSimplifyLine() {
605 SkASSERT(Type::kLine == fType);
606 SkASSERT(!fInheritedKey.count());
607 if (fStyle.isDashed()) {
608 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 }
615 // 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 }
627 if (fStyle.isSimpleFill()) {
628 this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty);
629 return;
630 }
631 if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
632 this->attemptToSimplifyStrokedLineToRRect()) {
633 return;
634 }
635 // Only path effects could care about the order of the points. Otherwise canonicalize
636 // the point order.
637 SkPoint* pts = fLineData.fPts;
638 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 }
641 }
642
attemptToSimplifyStrokedLineToRRect()643 bool 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 }
687