• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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