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