xref: /aosp_15_r20/external/skia/src/gpu/ganesh/geometry/GrStyledShape.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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,bool * inverted) const437 bool GrStyledShape::asRRect(SkRRect* rrect, bool* inverted) const {
438     if (!fShape.isRRect() && !fShape.isRect()) {
439         return false;
440     }
441 
442     if (rrect) {
443         *rrect = fShape.isRect() ? SkRRect::MakeRect(fShape.rect()) : fShape.rrect();
444     }
445 
446     if (inverted) {
447         *inverted = fShape.inverted();
448     }
449 
450     return true;
451 }
452 
asLine(SkPoint pts[2],bool * inverted) const453 bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
454     if (!fShape.isLine()) {
455         return false;
456     }
457 
458     if (pts) {
459         pts[0] = fShape.line().fP1;
460         pts[1] = fShape.line().fP2;
461     }
462     if (inverted) {
463         *inverted = fShape.inverted();
464     }
465     return true;
466 }
467 
asNestedRects(SkRect rects[2]) const468 bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
469     if (!fShape.isPath()) {
470         return false;
471     }
472 
473     // TODO: it would be better two store DRRects natively in the shape rather than converting
474     // them to a path and then reextracting the nested rects
475     if (fShape.path().isInverseFillType()) {
476         return false;
477     }
478 
479     SkPathDirection dirs[2];
480     if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
481         return false;
482     }
483 
484     if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
485         // The two rects need to be wound opposite to each other
486         return false;
487     }
488 
489     // Right now, nested rects where the margin is not the same width
490     // all around do not render correctly
491     const SkScalar* outer = rects[0].asScalars();
492     const SkScalar* inner = rects[1].asScalars();
493 
494     bool allEq = true;
495 
496     SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
497     bool allGoE1 = margin >= SK_Scalar1;
498 
499     for (int i = 1; i < 4; ++i) {
500         SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
501         if (temp < SK_Scalar1) {
502             allGoE1 = false;
503         }
504         if (!SkScalarNearlyEqual(margin, temp)) {
505             allEq = false;
506         }
507     }
508 
509     return allEq || allGoE1;
510 }
511 
512 class AutoRestoreInverseness {
513 public:
AutoRestoreInverseness(GrShape * shape,const GrStyle & style)514     AutoRestoreInverseness(GrShape* shape, const GrStyle& style)
515             // Dashing ignores inverseness skbug.com/5421.
516             : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
517 
~AutoRestoreInverseness()518     ~AutoRestoreInverseness() {
519         // Restore invertedness after any modifications were made to the shape type
520         fShape->setInverted(fInverted);
521         SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
522     }
523 
524 private:
525     GrShape* fShape;
526     bool fInverted;
527 };
528 
simplify()529 void GrStyledShape::simplify() {
530     AutoRestoreInverseness ari(&fShape, fStyle);
531 
532     unsigned simplifyFlags = 0;
533     if (fStyle.isSimpleFill()) {
534         simplifyFlags = GrShape::kAll_Flags;
535     } else if (!fStyle.hasPathEffect()) {
536         // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
537         if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
538             simplifyFlags |= GrShape::kIgnoreWinding_Flag;
539         }
540         simplifyFlags |= GrShape::kMakeCanonical_Flag;
541     } // else if there's a path effect, every destructive simplification is disabledd
542 
543     // Remember if the original shape was closed; in the event we simplify to a point or line
544     // because of degenerate geometry, we need to update joins and caps.
545     GrShape::Type oldType = fShape.type();
546     fClosed = fShape.simplify(simplifyFlags);
547     fSimplified = oldType != fShape.type();
548 
549     if (fShape.isPath()) {
550         // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
551         if (fInheritedKey.count() || fShape.path().isVolatile()) {
552             fGenID = 0;
553         } else {
554             fGenID = fShape.path().getGenerationID();
555         }
556         if (!fStyle.hasNonDashPathEffect() &&
557             (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
558              fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style ||
559              fShape.path().isConvex())) {
560             // Stroke styles don't differentiate between winding and even/odd. There is no
561             // distinction between even/odd and non-zero winding count for convex paths.
562             // Moreover, dashing ignores inverseness (skbug.com/5421)
563             fShape.path().setFillType(GrShape::kDefaultFillType);
564         }
565     } else {
566         fInheritedKey.reset(0);
567         // Whenever we simplify to a non-path, break the chain so we no longer refer to the
568         // original path. This prevents attaching genID listeners to temporary paths created when
569         // drawing simple shapes.
570         fInheritedPathForListeners.reset();
571         // Further simplifications to the shape based on the style
572         this->simplifyStroke();
573     }
574 }
575 
simplifyStroke()576 void GrStyledShape::simplifyStroke() {
577     AutoRestoreInverseness ari(&fShape, fStyle);
578 
579     // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
580     // becomes a round rect.
581     if (!fStyle.hasPathEffect() && fShape.isRect() &&
582         fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
583         if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
584             (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
585              fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
586             // Bevel-stroked rect needs path rendering
587             return;
588         }
589 
590         SkScalar r = fStyle.strokeRec().getWidth() / 2;
591         fShape.rect().outset(r, r);
592         if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
593             // There's no dashing to worry about if we got here, so it's okay that this resets
594             // winding parameters
595             fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
596         }
597         fStyle = GrStyle::SimpleFill();
598         fSimplified = true;
599         return;
600     }
601 
602     // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
603     // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
604     if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
605         fStyle.strokeRec().isHairlineStyle()) {
606         return;
607     }
608 
609     // Tracks style simplifications, even if the geometry can't be further simplified.
610     bool styleSimplified = false;
611     if (fStyle.isDashed()) {
612         // For dashing a point, if the first interval is on, we can drop the dash and just draw
613         // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
614         bool dropDash = false;
615         if (fShape.isPoint()) {
616             dropDash = fStyle.dashIntervalCnt() > 0 &&
617                        SkToBool(fStyle.dashIntervals()[0]);
618         } else {
619             dropDash = true;
620             for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
621                 if (SkToBool(fStyle.dashIntervals()[i])) {
622                     // An off interval has non-zero length so this won't convert to a simple line
623                     dropDash = false;
624                     break;
625                 }
626             }
627         }
628 
629         if (!dropDash) {
630             return;
631         }
632         // Fall through to modifying the shape to respect the new stroke geometry
633         fStyle = GrStyle(fStyle.strokeRec(), nullptr);
634         // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
635         // we reset to be unclosed so we don't override the style based on joins later.
636         fClosed = false;
637         styleSimplified = true;
638     }
639 
640     // At this point, we're a line or point with no path effects. Any fill portion of the style
641     // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
642     if (fStyle.isSimpleFill()) {
643         fShape.reset();
644         fSimplified = true;
645         return;
646     } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
647         // Stroke only
648         SkStrokeRec rec = fStyle.strokeRec();
649         rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
650         fStyle = GrStyle(rec, nullptr);
651         styleSimplified = true;
652     }
653 
654     // A point or line that was formed by a degenerate closed shape needs its style updated to
655     // reflect the fact that it doesn't actually produce caps.
656     if (fClosed) {
657         SkPaint::Cap cap;
658         if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
659             // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
660             // turn. With round joins, this would make a semi-circle at each end, which is visually
661             // identical to a round cap on the reduced line geometry.
662             cap = SkPaint::kRound_Cap;
663         } else {
664             // If this were a closed line, the 180 degree turn either is a miter join that exceeds
665             // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
666             // of a 180 degreen corner is equivalent to a butt cap.
667             //  - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
668             //    it fits this closed line description (it is not two 90 degree turns that could
669             //    produce miter geometry).
670             cap = SkPaint::kButt_Cap;
671         }
672 
673         if (cap != fStyle.strokeRec().getCap() ||
674             SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) {
675             SkStrokeRec rec = fStyle.strokeRec();
676             rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter());
677             fStyle = GrStyle(rec, nullptr);
678             styleSimplified = true;
679         }
680     }
681 
682     if (fShape.isPoint()) {
683         // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
684         // doesn't draw anything, a round cap is an oval and a square cap is a square.
685         if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
686             fShape.reset();
687         } else {
688             SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
689             SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
690             r.outset(w, w);
691 
692             if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
693                 fShape.setRRect(SkRRect::MakeOval(r));
694             } else {
695                 fShape.setRect(r);
696             }
697         }
698     } else {
699         // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
700         // allowed rotation angle, this would work for any lines.
701         SkRect rect;
702         SkVector outset;
703         if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
704             rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
705             rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
706             rect.fTop = rect.fBottom = fShape.line().fP1.fY;
707             outset.fY = fStyle.strokeRec().getWidth() / 2.f;
708             outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
709         } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
710             rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
711             rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
712             rect.fLeft = rect.fRight = fShape.line().fP1.fX;
713             outset.fX = fStyle.strokeRec().getWidth() / 2.f;
714             outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
715         } else {
716             // Geometrically can't apply the style and turn into a fill, but might still be simpler
717             // than before based solely on changes to fStyle.
718             fSimplified |= styleSimplified;
719             return;
720         }
721         rect.outset(outset.fX, outset.fY);
722         if (rect.isEmpty()) {
723             fShape.reset();
724         } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
725             SkASSERT(outset.fX == outset.fY);
726             fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
727         } else {
728             fShape.setRect(rect);
729         }
730     }
731     // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
732     fStyle = GrStyle::SimpleFill();
733     fSimplified = true;
734 }
735