• 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 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
8 
9 #include "include/core/SkArc.h"
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkPathEffect.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkStrokeRec.h"
14 #include "include/private/SkIDChangeListener.h"
15 #include "include/private/base/SkAlign.h"
16 #include "include/private/base/SkDebug.h"
17 #include "include/private/base/SkMalloc.h"
18 #include "include/private/base/SkTo.h"
19 
20 #include <algorithm>
21 #include <cstring>
22 #include <utility>
23 
24 
operator =(const GrStyledShape & that)25 GrStyledShape& GrStyledShape::operator=(const GrStyledShape& that) {
26     fShape      = that.fShape;
27     fStyle      = that.fStyle;
28     fGenID      = that.fGenID;
29     fSimplified = that.fSimplified;
30 
31     fInheritedKey.reset(that.fInheritedKey.count());
32     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
33                       sizeof(uint32_t) * fInheritedKey.count());
34     if (that.fInheritedPathForListeners.isValid()) {
35         fInheritedPathForListeners.set(*that.fInheritedPathForListeners);
36     } else {
37         fInheritedPathForListeners.reset();
38     }
39     return *this;
40 }
41 
is_inverted(bool originalIsInverted,GrStyledShape::FillInversion inversion)42 static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion) {
43     switch (inversion) {
44         case GrStyledShape::FillInversion::kPreserve:
45             return originalIsInverted;
46         case GrStyledShape::FillInversion::kFlip:
47             return !originalIsInverted;
48         case GrStyledShape::FillInversion::kForceInverted:
49             return true;
50         case GrStyledShape::FillInversion::kForceNoninverted:
51             return false;
52     }
53     return false;
54 }
55 
MakeFilled(const GrStyledShape & original,FillInversion inversion)56 GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion) {
57     bool newIsInverted = is_inverted(original.fShape.inverted(), inversion);
58     if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) {
59         // By returning the original rather than falling through we can preserve any inherited style
60         // key. Otherwise, we wipe it out below since the style change invalidates it.
61         return original;
62     }
63     GrStyledShape result;
64     SkASSERT(result.fStyle.isSimpleFill());
65     if (original.fInheritedPathForListeners.isValid()) {
66         result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners);
67     }
68 
69     result.fShape = original.fShape;
70     result.fGenID = original.fGenID;
71     result.fShape.setInverted(newIsInverted);
72 
73     if (!original.style().isSimpleFill()) {
74         // Going from a non-filled style to fill may allow additional simplifications (e.g.
75         // closing an open rect that wasn't closed in the original shape because it had
76         // stroke style).
77         result.simplify();
78         // The above simplify() call only sets simplified to true if its geometry was changed,
79         // since it already sees its style as a simple fill. Since the original style was not a
80         // simple fill, MakeFilled always simplifies.
81         result.fSimplified = true;
82     }
83 
84     // Verify that lines/points were converted to empty by the style change
85     SkASSERT((!original.fShape.isLine() && !original.fShape.isPoint()) || result.fShape.isEmpty());
86 
87     // We don't copy the inherited key since it can contain path effect information that we just
88     // stripped.
89     return result;
90 }
91 
styledBounds() const92 SkRect GrStyledShape::styledBounds() const {
93     if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
94         return SkRect::MakeEmpty();
95     }
96 
97     SkRect bounds;
98     fStyle.adjustBounds(&bounds, this->bounds());
99     return bounds;
100 }
101 
102 // 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)103 static int path_key_from_data_size(const SkPath& path) {
104     const int verbCnt = path.countVerbs();
105     if (verbCnt > GrStyledShape::kMaxKeyFromDataVerbCnt) {
106         return -1;
107     }
108     const int pointCnt = path.countPoints();
109     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
110 
111     static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
112     static_assert(sizeof(SkScalar) == sizeof(uint32_t));
113     // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to
114     // a uint32_t length.
115     return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
116 }
117 
118 // Writes the path data key into the passed pointer.
write_path_key_from_data(const SkPath & path,uint32_t * origKey)119 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
120     uint32_t* key = origKey;
121     // The check below should take care of negative values casted positive.
122     const int verbCnt = path.countVerbs();
123     const int pointCnt = path.countPoints();
124     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
125     SkASSERT(verbCnt <= GrStyledShape::kMaxKeyFromDataVerbCnt);
126     SkASSERT(pointCnt && verbCnt);
127     *key++ = verbCnt;
128     memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
129     int verbKeySize = SkAlign4(verbCnt);
130     // pad out to uint32_t alignment using value that will stand out when debugging.
131     uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
132     memset(pad, 0xDE, verbKeySize - verbCnt);
133     key += verbKeySize >> 2;
134 
135     memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
136     static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
137     key += 2 * pointCnt;
138     sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
139     static_assert(sizeof(SkScalar) == sizeof(uint32_t));
140     SkDEBUGCODE(key += conicWeightCnt);
141     SkASSERT(key - origKey == path_key_from_data_size(path));
142 }
143 
unstyledKeySize() const144 int GrStyledShape::unstyledKeySize() const {
145     if (fInheritedKey.count()) {
146         return fInheritedKey.count();
147     }
148 
149     int count = 1; // Every key has the state flags from the GrShape
150     switch(fShape.type()) {
151         case GrShape::Type::kPoint:
152             static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t));
153             count += sizeof(SkPoint) / sizeof(uint32_t);
154             break;
155         case GrShape::Type::kRect:
156             static_assert(0 == sizeof(SkRect) % sizeof(uint32_t));
157             count += sizeof(SkRect) / sizeof(uint32_t);
158             break;
159         case GrShape::Type::kRRect:
160             static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
161             count += SkRRect::kSizeInMemory / sizeof(uint32_t);
162             break;
163         case GrShape::Type::kArc:
164             static_assert(0 == sizeof(SkArc) % sizeof(uint32_t));
165             count += sizeof(SkArc) / sizeof(uint32_t);
166             break;
167         case GrShape::Type::kLine:
168             static_assert(0 == sizeof(GrLineSegment) % sizeof(uint32_t));
169             count += sizeof(GrLineSegment) / sizeof(uint32_t);
170             break;
171         case GrShape::Type::kPath: {
172             if (0 == fGenID) {
173                 return -1; // volatile, so won't be keyed
174             }
175             int dataKeySize = path_key_from_data_size(fShape.path());
176             if (dataKeySize >= 0) {
177                 count += dataKeySize;
178             } else {
179                 count++; // Just adds the gen ID.
180             }
181             break; }
182         default:
183             // else it's empty, which just needs the state flags for its key
184             SkASSERT(fShape.isEmpty());
185     }
186     return count;
187 }
188 
writeUnstyledKey(uint32_t * key) const189 void GrStyledShape::writeUnstyledKey(uint32_t* key) const {
190     SkASSERT(this->unstyledKeySize());
191     SkDEBUGCODE(uint32_t* origKey = key;)
192     if (fInheritedKey.count()) {
193         memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
194         SkDEBUGCODE(key += fInheritedKey.count();)
195     } else {
196         // Dir and start are only used for rect and rrect shapes, so are not included in other
197         // shape type keys. Make sure that they are the defaults for other shapes so it doesn't
198         // matter that we universally include them in the flag key value.
199         SkASSERT((fShape.isRect() || fShape.isRRect()) ||
200                  (fShape.dir() == GrShape::kDefaultDir &&
201                   fShape.startIndex() == GrShape::kDefaultStart));
202 
203         // Every key starts with the state from the GrShape (this includes path fill type,
204         // and any tracked winding, start, inversion, as well as the class of geometry).
205         *key++ = fShape.stateKey();
206 
207         switch(fShape.type()) {
208             case GrShape::Type::kPath: {
209                 SkASSERT(fGenID != 0);
210                 // Ensure that the path's inversion matches our state so that the path's key suffices.
211                 SkASSERT(fShape.inverted() == fShape.path().isInverseFillType());
212 
213                 int dataKeySize = path_key_from_data_size(fShape.path());
214                 if (dataKeySize >= 0) {
215                     write_path_key_from_data(fShape.path(), key);
216                     return;
217                 } else {
218                     *key++ = fGenID;
219                 }
220                 break; }
221             case GrShape::Type::kPoint:
222                 memcpy(key, &fShape.point(), sizeof(SkPoint));
223                 key += sizeof(SkPoint) / sizeof(uint32_t);
224                 break;
225             case GrShape::Type::kRect:
226                 memcpy(key, &fShape.rect(), sizeof(SkRect));
227                 key += sizeof(SkRect) / sizeof(uint32_t);
228                 break;
229             case GrShape::Type::kRRect:
230                 fShape.rrect().writeToMemory(key);
231                 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
232                 break;
233             case GrShape::Type::kArc:
234                 // Write dense floats first
235                 memcpy(key, &fShape.arc(), sizeof(SkRect) + 2 * sizeof(float));
236                 key += (sizeof(SkArc) / sizeof(uint32_t) - 1);
237                 // Then write the final bool as an int, to make sure upper bits are set
238                 *key++ = fShape.arc().isWedge() ? 1 : 0;
239                 break;
240             case GrShape::Type::kLine:
241                 memcpy(key, &fShape.line(), sizeof(GrLineSegment));
242                 key += sizeof(GrLineSegment) / sizeof(uint32_t);
243                 break;
244             default:
245                 // Nothing other than the flag state is needed in the key for an empty shape
246                 SkASSERT(fShape.isEmpty());
247         }
248     }
249     SkASSERT(key - origKey == this->unstyledKeySize());
250 }
251 
setInheritedKey(const GrStyledShape & parent,GrStyle::Apply apply,SkScalar scale)252 void GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply apply,
253                                     SkScalar scale) {
254     SkASSERT(!fInheritedKey.count());
255     // If the output shape turns out to be simple, then we will just use its geometric key
256     if (fShape.isPath()) {
257         // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
258         // ApplyFullStyle(shape).
259         // The full key is structured as (geo,path_effect,stroke).
260         // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
261         // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
262         // and then append the style key (which should now be stroke only) at the end.
263         int parentCnt = parent.fInheritedKey.count();
264         bool useParentGeoKey = !parentCnt;
265         if (useParentGeoKey) {
266             parentCnt = parent.unstyledKeySize();
267             if (parentCnt < 0) {
268                 // The parent's geometry has no key so we will have no key.
269                 fGenID = 0;
270                 return;
271             }
272         }
273         uint32_t styleKeyFlags = 0;
274         if (parent.knownToBeClosed()) {
275             styleKeyFlags |= GrStyle::kClosed_KeyFlag;
276         }
277         if (parent.asLine(nullptr, nullptr)) {
278             styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
279         }
280         int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
281         if (styleCnt < 0) {
282             // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
283             // we try to get a key for the shape.
284             fGenID = 0;
285             return;
286         }
287         fInheritedKey.reset(parentCnt + styleCnt);
288         if (useParentGeoKey) {
289             // This will be the geo key.
290             parent.writeUnstyledKey(fInheritedKey.get());
291         } else {
292             // This should be (geo,path_effect).
293             memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
294                    parentCnt * sizeof(uint32_t));
295         }
296         // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
297         GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
298                           styleKeyFlags);
299     }
300 }
301 
originalPathForListeners() const302 const SkPath* GrStyledShape::originalPathForListeners() const {
303     if (fInheritedPathForListeners.isValid()) {
304         return fInheritedPathForListeners.get();
305     } else if (fShape.isPath() && !fShape.path().isVolatile()) {
306         return &fShape.path();
307     }
308     return nullptr;
309 }
310 
addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const311 void GrStyledShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const {
312     if (const auto* lp = this->originalPathForListeners()) {
313         SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
314     }
315 }
316 
MakeArc(const SkArc & arc,const GrStyle & style,DoSimplify doSimplify)317 GrStyledShape GrStyledShape::MakeArc(const SkArc& arc,
318                                      const GrStyle& style,
319                                      DoSimplify doSimplify) {
320     GrStyledShape result;
321     result.fShape.setArc(
322             SkArc::Make(arc.fOval.makeSorted(), arc.fStartAngle, arc.fSweepAngle, arc.fType));
323     result.fStyle = style;
324     if (doSimplify == DoSimplify::kYes) {
325         result.simplify();
326     }
327     return result;
328 }
329 
GrStyledShape(const GrStyledShape & that)330 GrStyledShape::GrStyledShape(const GrStyledShape& that)
331         : fShape(that.fShape)
332         , fStyle(that.fStyle)
333         , fGenID(that.fGenID)
334         , fSimplified(that.fSimplified) {
335     fInheritedKey.reset(that.fInheritedKey.count());
336     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
337                       sizeof(uint32_t) * fInheritedKey.count());
338     if (that.fInheritedPathForListeners.isValid()) {
339         fInheritedPathForListeners.set(*that.fInheritedPathForListeners);
340     }
341 }
342 
GrStyledShape(const GrStyledShape & parent,GrStyle::Apply apply,SkScalar scale)343 GrStyledShape::GrStyledShape(const GrStyledShape& parent, GrStyle::Apply apply, SkScalar scale) {
344     // TODO: Add some quantization of scale for better cache performance here or leave that up
345     // to caller?
346     // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
347     // stroke of a rect).
348     if (!parent.style().applies() ||
349         (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
350         *this = parent;
351         return;
352     }
353 
354     SkPathEffect* pe = parent.fStyle.pathEffect();
355     SkTLazy<SkPath> tmpPath;
356     const GrStyledShape* parentForKey = &parent;
357     SkTLazy<GrStyledShape> tmpParent;
358 
359     // Start out as an empty path that is filled in by the applied style
360     fShape.setPath(SkPath());
361 
362     if (pe) {
363         const SkPath* srcForPathEffect;
364         if (parent.fShape.isPath()) {
365             srcForPathEffect = &parent.fShape.path();
366         } else {
367             srcForPathEffect = tmpPath.init();
368             parent.asPath(tmpPath.get());
369         }
370         // Should we consider bounds? Would have to include in key, but it'd be nice to know
371         // if the bounds actually modified anything before including in key.
372         SkStrokeRec strokeRec = parent.fStyle.strokeRec();
373         if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect,
374                                                  scale)) {
375             tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
376             *this = tmpParent->applyStyle(apply, scale);
377             return;
378         }
379         // A path effect has access to change the res scale but we aren't expecting it to and it
380         // would mess up our key computation.
381         SkASSERT(scale == strokeRec.getResScale());
382         if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
383             // The intermediate shape may not be a general path. If we we're just applying
384             // the path effect then attemptToReduceFromPath would catch it. This means that
385             // when we subsequently applied the remaining strokeRec we would have a non-path
386             // parent shape that would be used to determine the the stroked path's key.
387             // We detect that case here and change parentForKey to a temporary that represents
388             // the simpler shape so that applying both path effect and the strokerec all at
389             // once produces the same key.
390             tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr));
391             tmpParent->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
392             if (!tmpPath.isValid()) {
393                 tmpPath.init();
394             }
395             tmpParent->asPath(tmpPath.get());
396             SkStrokeRec::InitStyle fillOrHairline;
397             // The parent shape may have simplified away the strokeRec, check for that here.
398             if (tmpParent->style().applies()) {
399                 SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline,
400                                                                     *tmpPath.get(), scale));
401             } else if (tmpParent->style().isSimpleFill()) {
402                 fillOrHairline = SkStrokeRec::kFill_InitStyle;
403             } else {
404                 SkASSERT(tmpParent.get()->style().isSimpleHairline());
405                 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
406             }
407             fStyle.resetToInitStyle(fillOrHairline);
408             parentForKey = tmpParent.get();
409         } else {
410             fStyle = GrStyle(strokeRec, nullptr);
411         }
412     } else {
413         const SkPath* srcForParentStyle;
414         if (parent.fShape.isPath()) {
415             srcForParentStyle = &parent.fShape.path();
416         } else {
417             srcForParentStyle = tmpPath.init();
418             parent.asPath(tmpPath.get());
419         }
420         SkStrokeRec::InitStyle fillOrHairline;
421         SkASSERT(parent.fStyle.applies());
422         SkASSERT(!parent.fStyle.pathEffect());
423         SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline,
424                                                  *srcForParentStyle, scale));
425         fStyle.resetToInitStyle(fillOrHairline);
426     }
427 
428     if (parent.fInheritedPathForListeners.isValid()) {
429         fInheritedPathForListeners.set(*parent.fInheritedPathForListeners);
430     } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) {
431         fInheritedPathForListeners.set(parent.fShape.path());
432     }
433     this->simplify();
434     this->setInheritedKey(*parentForKey, apply, scale);
435 }
436 
asRRect(SkRRect * rrect,SkPathDirection * dir,unsigned * start,bool * inverted) const437 bool GrStyledShape::asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start,
438                             bool* inverted) const {
439     if (!fShape.isRRect() && !fShape.isRect()) {
440         return false;
441     }
442 
443     // Validity check here, if we don't have a path effect on the style, we should have passed
444     // appropriate flags to GrShape::simplify() to have reset these parameters.
445     SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir &&
446                                         fShape.startIndex() == GrShape::kDefaultStart));
447 
448     // If the shape is a regular rect, map to round rect winding parameters, including accounting
449     // for the automatic sorting of edges that SkRRect::MakeRect() performs.
450     if (fShape.isRect()) {
451         if (rrect) {
452             *rrect = SkRRect::MakeRect(fShape.rect());
453         }
454         // Don't bother mapping these if we don't have a path effect, however.
455         if (!fStyle.hasPathEffect()) {
456             if (dir) {
457                 *dir = GrShape::kDefaultDir;
458             }
459             if (start) {
460                 *start = GrShape::kDefaultStart;
461             }
462         } else {
463             // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
464             // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
465             // rect edges. Thus, we may need to modify the rrect's start index and direction.
466             SkPathDirection rectDir = fShape.dir();
467             unsigned rectStart = fShape.startIndex();
468 
469             if (fShape.rect().fLeft > fShape.rect().fRight) {
470                 // Toggle direction, and modify index by mapping through the array
471                 static const unsigned kMapping[] = {1, 0, 3, 2};
472                 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
473                                                            : SkPathDirection::kCCW;
474                 rectStart = kMapping[rectStart];
475             }
476             if (fShape.rect().fTop > fShape.rect().fBottom) {
477                 // Toggle direction and map index by 3 - start
478                 // NOTE: if we earlier flipped for X as well, this results in no net direction
479                 // change and effectively flipping the start index to the diagonal corners of the
480                 // rect (matching what we'd expect for a rect with both X and Y flipped).
481                 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
482                                                            : SkPathDirection::kCCW;
483                 rectStart = 3 - rectStart;
484             }
485 
486             if (dir) {
487                 *dir = rectDir;
488             }
489             if (start) {
490                 // Convert to round rect indexing
491                 *start = 2 * rectStart;
492             }
493         }
494     } else {
495         // Straight forward export
496         if (rrect) {
497             *rrect = fShape.rrect();
498         }
499         if (dir) {
500             *dir = fShape.dir();
501         }
502         if (start) {
503             *start = fShape.startIndex();
504             // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special
505             // but we do for dashing placement
506             if (fShape.rrect().isOval()) {
507                 *start &= 0b110;
508             }
509         }
510     }
511 
512     if (inverted) {
513         *inverted = fShape.inverted();
514     }
515 
516     return true;
517 }
518 
asLine(SkPoint pts[2],bool * inverted) const519 bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
520     if (!fShape.isLine()) {
521         return false;
522     }
523 
524     if (pts) {
525         pts[0] = fShape.line().fP1;
526         pts[1] = fShape.line().fP2;
527     }
528     if (inverted) {
529         *inverted = fShape.inverted();
530     }
531     return true;
532 }
533 
asNestedRects(SkRect rects[2]) const534 bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
535     if (!fShape.isPath()) {
536         return false;
537     }
538 
539     // TODO: it would be better two store DRRects natively in the shape rather than converting
540     // them to a path and then reextracting the nested rects
541     if (fShape.path().isInverseFillType()) {
542         return false;
543     }
544 
545     SkPathDirection dirs[2];
546     if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
547         return false;
548     }
549 
550     if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
551         // The two rects need to be wound opposite to each other
552         return false;
553     }
554 
555     // Right now, nested rects where the margin is not the same width
556     // all around do not render correctly
557     const SkScalar* outer = rects[0].asScalars();
558     const SkScalar* inner = rects[1].asScalars();
559 
560     bool allEq = true;
561 
562     SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
563     bool allGoE1 = margin >= SK_Scalar1;
564 
565     for (int i = 1; i < 4; ++i) {
566         SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
567         if (temp < SK_Scalar1) {
568             allGoE1 = false;
569         }
570         if (!SkScalarNearlyEqual(margin, temp)) {
571             allEq = false;
572         }
573     }
574 
575     return allEq || allGoE1;
576 }
577 
578 class AutoRestoreInverseness {
579 public:
AutoRestoreInverseness(GrShape * shape,const GrStyle & style)580     AutoRestoreInverseness(GrShape* shape, const GrStyle& style)
581             // Dashing ignores inverseness skbug.com/5421.
582             : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
583 
~AutoRestoreInverseness()584     ~AutoRestoreInverseness() {
585         // Restore invertedness after any modifications were made to the shape type
586         fShape->setInverted(fInverted);
587         SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
588     }
589 
590 private:
591     GrShape* fShape;
592     bool fInverted;
593 };
594 
simplify()595 void GrStyledShape::simplify() {
596     AutoRestoreInverseness ari(&fShape, fStyle);
597 
598     unsigned simplifyFlags = 0;
599     if (fStyle.isSimpleFill()) {
600         simplifyFlags = GrShape::kAll_Flags;
601     } else if (!fStyle.hasPathEffect()) {
602         // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
603         if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
604             simplifyFlags |= GrShape::kIgnoreWinding_Flag;
605         }
606         simplifyFlags |= GrShape::kMakeCanonical_Flag;
607     } // else if there's a path effect, every destructive simplification is disabledd
608 
609     // Remember if the original shape was closed; in the event we simplify to a point or line
610     // because of degenerate geometry, we need to update joins and caps.
611     GrShape::Type oldType = fShape.type();
612     fClosed = fShape.simplify(simplifyFlags);
613     fSimplified = oldType != fShape.type();
614 
615     if (fShape.isPath()) {
616         // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
617         if (fInheritedKey.count() || fShape.path().isVolatile()) {
618             fGenID = 0;
619         } else {
620             fGenID = fShape.path().getGenerationID();
621         }
622         if (!fStyle.hasNonDashPathEffect() &&
623             (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
624              fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style ||
625              fShape.path().isConvex())) {
626             // Stroke styles don't differentiate between winding and even/odd. There is no
627             // distinction between even/odd and non-zero winding count for convex paths.
628             // Moreover, dashing ignores inverseness (skbug.com/5421)
629             fShape.path().setFillType(GrShape::kDefaultFillType);
630         }
631     } else {
632         fInheritedKey.reset(0);
633         // Whenever we simplify to a non-path, break the chain so we no longer refer to the
634         // original path. This prevents attaching genID listeners to temporary paths created when
635         // drawing simple shapes.
636         fInheritedPathForListeners.reset();
637         // Further simplifications to the shape based on the style
638         this->simplifyStroke();
639     }
640 }
641 
simplifyStroke()642 void GrStyledShape::simplifyStroke() {
643     AutoRestoreInverseness ari(&fShape, fStyle);
644 
645     // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
646     // becomes a round rect.
647     if (!fStyle.hasPathEffect() && fShape.isRect() &&
648         fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
649         if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
650             (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
651              fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
652             // Bevel-stroked rect needs path rendering
653             return;
654         }
655 
656         SkScalar r = fStyle.strokeRec().getWidth() / 2;
657         fShape.rect().outset(r, r);
658         if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
659             // There's no dashing to worry about if we got here, so it's okay that this resets
660             // winding parameters
661             fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
662         }
663         fStyle = GrStyle::SimpleFill();
664         fSimplified = true;
665         return;
666     }
667 
668     // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
669     // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
670     if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
671         fStyle.strokeRec().isHairlineStyle()) {
672         return;
673     }
674 
675     // Tracks style simplifications, even if the geometry can't be further simplified.
676     bool styleSimplified = false;
677     if (fStyle.isDashed()) {
678         // For dashing a point, if the first interval is on, we can drop the dash and just draw
679         // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
680         bool dropDash = false;
681         if (fShape.isPoint()) {
682             dropDash = fStyle.dashIntervalCnt() > 0 &&
683                        SkToBool(fStyle.dashIntervals()[0]);
684         } else {
685             dropDash = true;
686             for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
687                 if (SkToBool(fStyle.dashIntervals()[i])) {
688                     // An off interval has non-zero length so this won't convert to a simple line
689                     dropDash = false;
690                     break;
691                 }
692             }
693         }
694 
695         if (!dropDash) {
696             return;
697         }
698         // Fall through to modifying the shape to respect the new stroke geometry
699         fStyle = GrStyle(fStyle.strokeRec(), nullptr);
700         // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
701         // we reset to be unclosed so we don't override the style based on joins later.
702         fClosed = false;
703         styleSimplified = true;
704     }
705 
706     // At this point, we're a line or point with no path effects. Any fill portion of the style
707     // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
708     if (fStyle.isSimpleFill()) {
709         fShape.reset();
710         fSimplified = true;
711         return;
712     } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
713         // Stroke only
714         SkStrokeRec rec = fStyle.strokeRec();
715         rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
716         fStyle = GrStyle(rec, nullptr);
717         styleSimplified = true;
718     }
719 
720     // A point or line that was formed by a degenerate closed shape needs its style updated to
721     // reflect the fact that it doesn't actually produce caps.
722     if (fClosed) {
723         SkPaint::Cap cap;
724         if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
725             // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
726             // turn. With round joins, this would make a semi-circle at each end, which is visually
727             // identical to a round cap on the reduced line geometry.
728             cap = SkPaint::kRound_Cap;
729         } else {
730             // If this were a closed line, the 180 degree turn either is a miter join that exceeds
731             // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
732             // of a 180 degreen corner is equivalent to a butt cap.
733             //  - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
734             //    it fits this closed line description (it is not two 90 degree turns that could
735             //    produce miter geometry).
736             cap = SkPaint::kButt_Cap;
737         }
738 
739         if (cap != fStyle.strokeRec().getCap() ||
740             SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) {
741             SkStrokeRec rec = fStyle.strokeRec();
742             rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter());
743             fStyle = GrStyle(rec, nullptr);
744             styleSimplified = true;
745         }
746     }
747 
748     if (fShape.isPoint()) {
749         // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
750         // doesn't draw anything, a round cap is an oval and a square cap is a square.
751         if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
752             fShape.reset();
753         } else {
754             SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
755             SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
756             r.outset(w, w);
757 
758             if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
759                 fShape.setRRect(SkRRect::MakeOval(r));
760             } else {
761                 fShape.setRect(r);
762             }
763         }
764     } else {
765         // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
766         // allowed rotation angle, this would work for any lines.
767         SkRect rect;
768         SkVector outset;
769         if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
770             rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
771             rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
772             rect.fTop = rect.fBottom = fShape.line().fP1.fY;
773             outset.fY = fStyle.strokeRec().getWidth() / 2.f;
774             outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
775         } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
776             rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
777             rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
778             rect.fLeft = rect.fRight = fShape.line().fP1.fX;
779             outset.fX = fStyle.strokeRec().getWidth() / 2.f;
780             outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
781         } else {
782             // Geometrically can't apply the style and turn into a fill, but might still be simpler
783             // than before based solely on changes to fStyle.
784             fSimplified |= styleSimplified;
785             return;
786         }
787         rect.outset(outset.fX, outset.fY);
788         if (rect.isEmpty()) {
789             fShape.reset();
790         } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
791             SkASSERT(outset.fX == outset.fY);
792             fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
793         } else {
794             fShape.setRect(rect);
795         }
796     }
797     // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
798     fStyle = GrStyle::SimpleFill();
799     fSimplified = true;
800 }
801