• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrShape.h"
9 
10 #include <utility>
11 
operator =(const GrShape & that)12 GrShape& GrShape::operator=(const GrShape& that) {
13     fStyle = that.fStyle;
14     this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
15     switch (fType) {
16         case Type::kEmpty:
17             break;
18         case Type::kInvertedEmpty:
19             break;
20         case Type::kRRect:
21             fRRectData = that.fRRectData;
22             break;
23         case Type::kArc:
24             fArcData = that.fArcData;
25             break;
26         case Type::kLine:
27             fLineData = that.fLineData;
28             break;
29         case Type::kPath:
30             fPathData.fGenID = that.fPathData.fGenID;
31             break;
32     }
33     fInheritedKey.reset(that.fInheritedKey.count());
34     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
35                       sizeof(uint32_t) * fInheritedKey.count());
36     if (that.fInheritedPathForListeners.isValid()) {
37         fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
38     } else {
39         fInheritedPathForListeners.reset();
40     }
41     return *this;
42 }
43 
flip_inversion(bool originalIsInverted,GrShape::FillInversion inversion)44 static bool flip_inversion(bool originalIsInverted, GrShape::FillInversion inversion) {
45     switch (inversion) {
46         case GrShape::FillInversion::kPreserve:
47             return false;
48         case GrShape::FillInversion::kFlip:
49             return true;
50         case GrShape::FillInversion::kForceInverted:
51             return !originalIsInverted;
52         case GrShape::FillInversion::kForceNoninverted:
53             return originalIsInverted;
54     }
55     return false;
56 }
57 
is_inverted(bool originalIsInverted,GrShape::FillInversion inversion)58 static bool is_inverted(bool originalIsInverted, GrShape::FillInversion inversion) {
59     switch (inversion) {
60         case GrShape::FillInversion::kPreserve:
61             return originalIsInverted;
62         case GrShape::FillInversion::kFlip:
63             return !originalIsInverted;
64         case GrShape::FillInversion::kForceInverted:
65             return true;
66         case GrShape::FillInversion::kForceNoninverted:
67             return false;
68     }
69     return false;
70 }
71 
MakeFilled(const GrShape & original,FillInversion inversion)72 GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
73     if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) {
74         // By returning the original rather than falling through we can preserve any inherited style
75         // key. Otherwise, we wipe it out below since the style change invalidates it.
76         return original;
77     }
78     GrShape result;
79     if (original.fInheritedPathForListeners.isValid()) {
80         result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get());
81     }
82     switch (original.fType) {
83         case Type::kRRect:
84             result.fType = original.fType;
85             result.fRRectData.fRRect = original.fRRectData.fRRect;
86             result.fRRectData.fDir = kDefaultRRectDir;
87             result.fRRectData.fStart = kDefaultRRectStart;
88             result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
89             break;
90         case Type::kArc:
91             result.fType = original.fType;
92             result.fArcData.fOval = original.fArcData.fOval;
93             result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees;
94             result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees;
95             result.fArcData.fUseCenter = original.fArcData.fUseCenter;
96             result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion);
97             break;
98         case Type::kLine:
99             // Lines don't fill.
100             if (is_inverted(original.fLineData.fInverted, inversion)) {
101                 result.fType = Type::kInvertedEmpty;
102             } else {
103                 result.fType = Type::kEmpty;
104             }
105             break;
106         case Type::kEmpty:
107             result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty :  Type::kEmpty;
108             break;
109         case Type::kInvertedEmpty:
110             result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty :  Type::kEmpty;
111             break;
112         case Type::kPath:
113             result.initType(Type::kPath, &original.fPathData.fPath);
114             result.fPathData.fGenID = original.fPathData.fGenID;
115             if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) {
116                 result.fPathData.fPath.toggleInverseFillType();
117             }
118             if (!original.style().isSimpleFill()) {
119                 // Going from a non-filled style to fill may allow additional simplifications (e.g.
120                 // closing an open rect that wasn't closed in the original shape because it had
121                 // stroke style).
122                 result.attemptToSimplifyPath();
123             }
124             break;
125     }
126     // We don't copy the inherited key since it can contain path effect information that we just
127     // stripped.
128     return result;
129 }
130 
bounds() const131 SkRect GrShape::bounds() const {
132     // Bounds where left == bottom or top == right can indicate a line or point shape. We return
133     // inverted bounds for a truly empty shape.
134     static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
135     switch (fType) {
136         case Type::kEmpty:
137             return kInverted;
138         case Type::kInvertedEmpty:
139             return kInverted;
140         case Type::kLine: {
141             SkRect bounds;
142             if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
143                 bounds.fLeft = fLineData.fPts[0].fX;
144                 bounds.fRight = fLineData.fPts[1].fX;
145             } else {
146                 bounds.fLeft = fLineData.fPts[1].fX;
147                 bounds.fRight = fLineData.fPts[0].fX;
148             }
149             if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) {
150                 bounds.fTop = fLineData.fPts[0].fY;
151                 bounds.fBottom = fLineData.fPts[1].fY;
152             } else {
153                 bounds.fTop = fLineData.fPts[1].fY;
154                 bounds.fBottom = fLineData.fPts[0].fY;
155             }
156             return bounds;
157         }
158         case Type::kRRect:
159             return fRRectData.fRRect.getBounds();
160         case Type::kArc:
161             // Could make this less conservative by looking at angles.
162             return fArcData.fOval;
163         case Type::kPath:
164             return this->path().getBounds();
165     }
166     SK_ABORT("Unknown shape type");
167     return kInverted;
168 }
169 
styledBounds() const170 SkRect GrShape::styledBounds() const {
171     if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
172         return SkRect::MakeEmpty();
173     }
174 
175     SkRect bounds;
176     fStyle.adjustBounds(&bounds, this->bounds());
177     return bounds;
178 }
179 
180 // 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)181 static int path_key_from_data_size(const SkPath& path) {
182     const int verbCnt = path.countVerbs();
183     if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) {
184         return -1;
185     }
186     const int pointCnt = path.countPoints();
187     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
188 
189     GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
190     GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
191     // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to
192     // a uint32_t length.
193     return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
194 }
195 
196 // Writes the path data key into the passed pointer.
write_path_key_from_data(const SkPath & path,uint32_t * origKey)197 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
198     uint32_t* key = origKey;
199     // The check below should take care of negative values casted positive.
200     const int verbCnt = path.countVerbs();
201     const int pointCnt = path.countPoints();
202     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
203     SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt);
204     SkASSERT(pointCnt && verbCnt);
205     *key++ = path.getFillType();
206     *key++ = verbCnt;
207     memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
208     int verbKeySize = SkAlign4(verbCnt);
209     // pad out to uint32_t alignment using value that will stand out when debugging.
210     uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
211     memset(pad, 0xDE, verbKeySize - verbCnt);
212     key += verbKeySize >> 2;
213 
214     memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
215     GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
216     key += 2 * pointCnt;
217     sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
218     GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
219     SkDEBUGCODE(key += conicWeightCnt);
220     SkASSERT(key - origKey == path_key_from_data_size(path));
221 }
222 
unstyledKeySize() const223 int GrShape::unstyledKeySize() const {
224     if (fInheritedKey.count()) {
225         return fInheritedKey.count();
226     }
227     switch (fType) {
228         case Type::kEmpty:
229             return 1;
230         case Type::kInvertedEmpty:
231             return 1;
232         case Type::kRRect:
233             SkASSERT(!fInheritedKey.count());
234             GR_STATIC_ASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
235             // + 1 for the direction, start index, and inverseness.
236             return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
237         case Type::kArc:
238             SkASSERT(!fInheritedKey.count());
239             GR_STATIC_ASSERT(0 == sizeof(fArcData) % sizeof(uint32_t));
240             return sizeof(fArcData) / sizeof(uint32_t);
241         case Type::kLine:
242             GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
243             // 4 for the end points and 1 for the inverseness
244             return 5;
245         case Type::kPath: {
246             if (0 == fPathData.fGenID) {
247                 return -1;
248             }
249             int dataKeySize = path_key_from_data_size(fPathData.fPath);
250             if (dataKeySize >= 0) {
251                 return dataKeySize;
252             }
253             // The key is the path ID and fill type.
254             return 2;
255         }
256     }
257     SK_ABORT("Should never get here.");
258     return 0;
259 }
260 
writeUnstyledKey(uint32_t * key) const261 void GrShape::writeUnstyledKey(uint32_t* key) const {
262     SkASSERT(this->unstyledKeySize());
263     SkDEBUGCODE(uint32_t* origKey = key;)
264     if (fInheritedKey.count()) {
265         memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
266         SkDEBUGCODE(key += fInheritedKey.count();)
267     } else {
268         switch (fType) {
269             case Type::kEmpty:
270                 *key++ = 1;
271                 break;
272             case Type::kInvertedEmpty:
273                 *key++ = 2;
274                 break;
275             case Type::kRRect:
276                 fRRectData.fRRect.writeToMemory(key);
277                 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
278                 *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
279                 *key |= fRRectData.fInverted ? (1 << 30) : 0;
280                 *key++ |= fRRectData.fStart;
281                 SkASSERT(fRRectData.fStart < 8);
282                 break;
283             case Type::kArc:
284                 memcpy(key, &fArcData, sizeof(fArcData));
285                 key += sizeof(fArcData) / sizeof(uint32_t);
286                 break;
287             case Type::kLine:
288                 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
289                 key += 4;
290                 *key++ = fLineData.fInverted ? 1 : 0;
291                 break;
292             case Type::kPath: {
293                 SkASSERT(fPathData.fGenID);
294                 int dataKeySize = path_key_from_data_size(fPathData.fPath);
295                 if (dataKeySize >= 0) {
296                     write_path_key_from_data(fPathData.fPath, key);
297                     return;
298                 }
299                 *key++ = fPathData.fGenID;
300                 // We could canonicalize the fill rule for paths that don't differentiate between
301                 // even/odd or winding fill (e.g. convex).
302                 *key++ = this->path().getFillType();
303                 break;
304             }
305         }
306     }
307     SkASSERT(key - origKey == this->unstyledKeySize());
308 }
309 
setInheritedKey(const GrShape & parent,GrStyle::Apply apply,SkScalar scale)310 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
311     SkASSERT(!fInheritedKey.count());
312     // If the output shape turns out to be simple, then we will just use its geometric key
313     if (Type::kPath == fType) {
314         // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
315         // ApplyFullStyle(shape).
316         // The full key is structured as (geo,path_effect,stroke).
317         // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
318         // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
319         // and then append the style key (which should now be stroke only) at the end.
320         int parentCnt = parent.fInheritedKey.count();
321         bool useParentGeoKey = !parentCnt;
322         if (useParentGeoKey) {
323             parentCnt = parent.unstyledKeySize();
324             if (parentCnt < 0) {
325                 // The parent's geometry has no key so we will have no key.
326                 fPathData.fGenID = 0;
327                 return;
328             }
329         }
330         uint32_t styleKeyFlags = 0;
331         if (parent.knownToBeClosed()) {
332             styleKeyFlags |= GrStyle::kClosed_KeyFlag;
333         }
334         if (parent.asLine(nullptr, nullptr)) {
335             styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
336         }
337         int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
338         if (styleCnt < 0) {
339             // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
340             // we try to get a key for the shape.
341             fPathData.fGenID = 0;
342             return;
343         }
344         fInheritedKey.reset(parentCnt + styleCnt);
345         if (useParentGeoKey) {
346             // This will be the geo key.
347             parent.writeUnstyledKey(fInheritedKey.get());
348         } else {
349             // This should be (geo,path_effect).
350             memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
351                    parentCnt * sizeof(uint32_t));
352         }
353         // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
354         GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
355                           styleKeyFlags);
356     }
357 }
358 
originalPathForListeners() const359 const SkPath* GrShape::originalPathForListeners() const {
360     if (fInheritedPathForListeners.isValid()) {
361         return fInheritedPathForListeners.get();
362     } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) {
363         return &fPathData.fPath;
364     }
365     return nullptr;
366 }
367 
addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener> listener) const368 void GrShape::addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener> listener) const {
369     if (const auto* lp = this->originalPathForListeners()) {
370         SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
371     }
372 }
373 
MakeArc(const SkRect & oval,SkScalar startAngleDegrees,SkScalar sweepAngleDegrees,bool useCenter,const GrStyle & style)374 GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees,
375                          bool useCenter, const GrStyle& style) {
376     GrShape result;
377     result.changeType(Type::kArc);
378     result.fArcData.fOval = oval;
379     result.fArcData.fStartAngleDegrees = startAngleDegrees;
380     result.fArcData.fSweepAngleDegrees = sweepAngleDegrees;
381     result.fArcData.fUseCenter = useCenter;
382     result.fArcData.fInverted = false;
383     result.fStyle = style;
384     result.attemptToSimplifyArc();
385     return result;
386 }
387 
GrShape(const GrShape & that)388 GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
389     const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
390     this->initType(that.fType, thatPath);
391     switch (fType) {
392         case Type::kEmpty:
393             break;
394         case Type::kInvertedEmpty:
395             break;
396         case Type::kRRect:
397             fRRectData = that.fRRectData;
398             break;
399         case Type::kArc:
400             fArcData = that.fArcData;
401             break;
402         case Type::kLine:
403             fLineData = that.fLineData;
404             break;
405         case Type::kPath:
406             fPathData.fGenID = that.fPathData.fGenID;
407             break;
408     }
409     fInheritedKey.reset(that.fInheritedKey.count());
410     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
411                       sizeof(uint32_t) * fInheritedKey.count());
412     if (that.fInheritedPathForListeners.isValid()) {
413         fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
414     }
415 }
416 
GrShape(const GrShape & parent,GrStyle::Apply apply,SkScalar scale)417 GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
418     // TODO: Add some quantization of scale for better cache performance here or leave that up
419     // to caller?
420     // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
421     // stroke of a rect).
422     if (!parent.style().applies() ||
423         (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
424         this->initType(Type::kEmpty);
425         *this = parent;
426         return;
427     }
428 
429     SkPathEffect* pe = parent.fStyle.pathEffect();
430     SkTLazy<SkPath> tmpPath;
431     const GrShape* parentForKey = &parent;
432     SkTLazy<GrShape> tmpParent;
433     this->initType(Type::kPath);
434     fPathData.fGenID = 0;
435     if (pe) {
436         const SkPath* srcForPathEffect;
437         if (parent.fType == Type::kPath) {
438             srcForPathEffect = &parent.path();
439         } else {
440             srcForPathEffect = tmpPath.init();
441             parent.asPath(tmpPath.get());
442         }
443         // Should we consider bounds? Would have to include in key, but it'd be nice to know
444         // if the bounds actually modified anything before including in key.
445         SkStrokeRec strokeRec = parent.fStyle.strokeRec();
446         if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
447                                                  scale)) {
448             tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
449             *this = tmpParent.get()->applyStyle(apply, scale);
450             return;
451         }
452         // A path effect has access to change the res scale but we aren't expecting it to and it
453         // would mess up our key computation.
454         SkASSERT(scale == strokeRec.getResScale());
455         if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
456             // The intermediate shape may not be a general path. If we we're just applying
457             // the path effect then attemptToReduceFromPath would catch it. This means that
458             // when we subsequently applied the remaining strokeRec we would have a non-path
459             // parent shape that would be used to determine the the stroked path's key.
460             // We detect that case here and change parentForKey to a temporary that represents
461             // the simpler shape so that applying both path effect and the strokerec all at
462             // once produces the same key.
463             tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
464             tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
465             if (!tmpPath.isValid()) {
466                 tmpPath.init();
467             }
468             tmpParent.get()->asPath(tmpPath.get());
469             SkStrokeRec::InitStyle fillOrHairline;
470             // The parent shape may have simplified away the strokeRec, check for that here.
471             if (tmpParent.get()->style().applies()) {
472                 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
473                                                                     *tmpPath.get(), scale));
474             } else if (tmpParent.get()->style().isSimpleFill()) {
475                 fillOrHairline = SkStrokeRec::kFill_InitStyle;
476             } else {
477                 SkASSERT(tmpParent.get()->style().isSimpleHairline());
478                 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
479             }
480             fStyle.resetToInitStyle(fillOrHairline);
481             parentForKey = tmpParent.get();
482         } else {
483             fStyle = GrStyle(strokeRec, nullptr);
484         }
485     } else {
486         const SkPath* srcForParentStyle;
487         if (parent.fType == Type::kPath) {
488             srcForParentStyle = &parent.path();
489         } else {
490             srcForParentStyle = tmpPath.init();
491             parent.asPath(tmpPath.get());
492         }
493         SkStrokeRec::InitStyle fillOrHairline;
494         SkASSERT(parent.fStyle.applies());
495         SkASSERT(!parent.fStyle.pathEffect());
496         SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
497                                                  scale));
498         fStyle.resetToInitStyle(fillOrHairline);
499     }
500     if (parent.fInheritedPathForListeners.isValid()) {
501         fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get());
502     } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) {
503         fInheritedPathForListeners.set(parent.fPathData.fPath);
504     }
505     this->attemptToSimplifyPath();
506     this->setInheritedKey(*parentForKey, apply, scale);
507 }
508 
attemptToSimplifyPath()509 void GrShape::attemptToSimplifyPath() {
510     SkRect rect;
511     SkRRect rrect;
512     SkPath::Direction rrectDir;
513     unsigned rrectStart;
514     bool inverted = this->path().isInverseFillType();
515     SkPoint pts[2];
516     if (this->path().isEmpty()) {
517         // Dashing ignores inverseness skbug.com/5421.
518         this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty
519                                                                : Type::kEmpty);
520     } else if (this->path().isLine(pts)) {
521         this->changeType(Type::kLine);
522         fLineData.fPts[0] = pts[0];
523         fLineData.fPts[1] = pts[1];
524         fLineData.fInverted = inverted;
525     } else if (SkPathPriv::IsRRect(this->path(), &rrect, &rrectDir, &rrectStart)) {
526         this->changeType(Type::kRRect);
527         fRRectData.fRRect = rrect;
528         fRRectData.fDir = rrectDir;
529         fRRectData.fStart = rrectStart;
530         fRRectData.fInverted = inverted;
531         SkASSERT(!fRRectData.fRRect.isEmpty());
532     } else if (SkPathPriv::IsOval(this->path(), &rect, &rrectDir, &rrectStart)) {
533         this->changeType(Type::kRRect);
534         fRRectData.fRRect.setOval(rect);
535         fRRectData.fDir = rrectDir;
536         fRRectData.fInverted = inverted;
537         // convert from oval indexing to rrect indexiing.
538         fRRectData.fStart = 2 * rrectStart;
539     } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
540         this->changeType(Type::kRRect);
541         // When there is a path effect we restrict rect detection to the narrower API that
542         // gives us the starting position. Otherwise, we will retry with the more aggressive
543         // isRect().
544         fRRectData.fRRect.setRect(rect);
545         fRRectData.fInverted = inverted;
546         fRRectData.fDir = rrectDir;
547         // convert from rect indexing to rrect indexiing.
548         fRRectData.fStart = 2 * rrectStart;
549     } else if (!this->style().hasPathEffect()) {
550         bool closed;
551         if (this->path().isRect(&rect, &closed, nullptr)) {
552             if (closed || this->style().isSimpleFill()) {
553                 this->changeType(Type::kRRect);
554                 fRRectData.fRRect.setRect(rect);
555                 // Since there is no path effect the dir and start index is immaterial.
556                 fRRectData.fDir = kDefaultRRectDir;
557                 fRRectData.fStart = kDefaultRRectStart;
558                 // There isn't dashing so we will have to preserver inverseness.
559                 fRRectData.fInverted = inverted;
560             }
561         }
562     }
563     if (Type::kPath != fType) {
564         fInheritedKey.reset(0);
565         // Whenever we simplify to a non-path, break the chain so we no longer refer to the
566         // original path. This prevents attaching genID listeners to temporary paths created when
567         // drawing simple shapes.
568         fInheritedPathForListeners.reset();
569         if (Type::kRRect == fType) {
570             this->attemptToSimplifyRRect();
571         } else if (Type::kLine == fType) {
572             this->attemptToSimplifyLine();
573         }
574     } else {
575         if (fInheritedKey.count() || this->path().isVolatile()) {
576             fPathData.fGenID = 0;
577         } else {
578             fPathData.fGenID = this->path().getGenerationID();
579         }
580         if (!this->style().hasNonDashPathEffect()) {
581             if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
582                 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
583                 // Stroke styles don't differentiate between winding and even/odd.
584                 // Moreover, dashing ignores inverseness (skbug.com/5421)
585                 bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
586                 if (inverse) {
587                     this->path().setFillType(kDefaultPathInverseFillType);
588                 } else {
589                     this->path().setFillType(kDefaultPathFillType);
590                 }
591             } else if (this->path().isConvex()) {
592                 // There is no distinction between even/odd and non-zero winding count for convex
593                 // paths.
594                 if (this->path().isInverseFillType()) {
595                     this->path().setFillType(kDefaultPathInverseFillType);
596                 } else {
597                     this->path().setFillType(kDefaultPathFillType);
598                 }
599             }
600         }
601     }
602 }
603 
attemptToSimplifyRRect()604 void GrShape::attemptToSimplifyRRect() {
605     SkASSERT(Type::kRRect == fType);
606     SkASSERT(!fInheritedKey.count());
607     if (fRRectData.fRRect.isEmpty()) {
608         // An empty filled rrect is equivalent to a filled empty path with inversion preserved.
609         if (fStyle.isSimpleFill()) {
610             fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty;
611             fStyle = GrStyle::SimpleFill();
612             return;
613         }
614         // Dashing a rrect with no width or height is equivalent to filling an emtpy path.
615         // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length
616         // line  will produce cap geometry if the effect begins in an "on" interval.
617         if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) {
618             // Dashing ignores the inverseness (currently). skbug.com/5421.
619             fType = Type::kEmpty;
620             fStyle = GrStyle::SimpleFill();
621             return;
622         }
623     }
624     if (!this->style().hasPathEffect()) {
625         fRRectData.fDir = kDefaultRRectDir;
626         fRRectData.fStart = kDefaultRRectStart;
627     } else if (fStyle.isDashed()) {
628         // Dashing ignores the inverseness (currently). skbug.com/5421
629         fRRectData.fInverted = false;
630         // Possible TODO here: Check whether the dash results in a single arc or line.
631     }
632     // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
633     if (!fStyle.hasPathEffect() &&
634         fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style &&
635         fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
636         fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 &&
637         fRRectData.fRRect.isRect()) {
638         SkScalar r = fStyle.strokeRec().getWidth() / 2;
639         fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r));
640         fStyle = GrStyle::SimpleFill();
641     }
642 }
643 
attemptToSimplifyLine()644 void GrShape::attemptToSimplifyLine() {
645     SkASSERT(Type::kLine == fType);
646     SkASSERT(!fInheritedKey.count());
647     if (fStyle.isDashed()) {
648         bool allOffsZero = true;
649         for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) {
650             allOffsZero = !fStyle.dashIntervals()[i];
651         }
652         if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) {
653             return;
654         }
655         // Dashing ignores inverseness.
656         fLineData.fInverted = false;
657         return;
658     } else if (fStyle.hasPathEffect()) {
659         return;
660     }
661     if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
662         // Make stroke + fill be stroke since the fill is empty.
663         SkStrokeRec rec = fStyle.strokeRec();
664         rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
665         fStyle = GrStyle(rec, nullptr);
666     }
667     if (fStyle.isSimpleFill()) {
668         this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty);
669         return;
670     }
671     if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
672         this->attemptToSimplifyStrokedLineToRRect()) {
673         return;
674     }
675     // Only path effects could care about the order of the points. Otherwise canonicalize
676     // the point order.
677     SkPoint* pts = fLineData.fPts;
678     if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
679         using std::swap;
680         swap(pts[0], pts[1]);
681     }
682 }
683 
attemptToSimplifyArc()684 void GrShape::attemptToSimplifyArc() {
685     SkASSERT(fType == Type::kArc);
686     SkASSERT(!fArcData.fInverted);
687     if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) {
688         this->changeType(Type::kEmpty);
689         return;
690     }
691 
692     // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses
693     // the full circle and doesn't use the center point is an oval. Unless it has square or round
694     // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're
695     // ignoring that for now.
696     if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter &&
697                                   fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) {
698         if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) {
699             auto oval = fArcData.fOval;
700             this->changeType(Type::kRRect);
701             this->fRRectData.fRRect.setOval(oval);
702             this->fRRectData.fDir = kDefaultRRectDir;
703             this->fRRectData.fStart = kDefaultRRectStart;
704             this->fRRectData.fInverted = false;
705             return;
706         }
707     }
708     if (!fStyle.pathEffect()) {
709         // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always
710         // positive.
711         if (fArcData.fSweepAngleDegrees < 0) {
712             fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees;
713             fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees;
714         }
715     }
716     if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) {
717         this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f);
718     }
719     // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to
720     // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke
721     // could as well if the stroke fills the center.
722 }
723 
attemptToSimplifyStrokedLineToRRect()724 bool GrShape::attemptToSimplifyStrokedLineToRRect() {
725     SkASSERT(Type::kLine == fType);
726     SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
727 
728     SkRect rect;
729     SkVector outset;
730     // If we allowed a rotation angle for rrects we could capture all cases here.
731     if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) {
732         rect.fLeft = SkTMin(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
733         rect.fRight = SkTMax(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
734         rect.fTop = rect.fBottom = fLineData.fPts[0].fY;
735         outset.fY = fStyle.strokeRec().getWidth() / 2.f;
736         outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
737     } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) {
738         rect.fTop = SkTMin(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
739         rect.fBottom = SkTMax(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
740         rect.fLeft = rect.fRight = fLineData.fPts[0].fX;
741         outset.fX = fStyle.strokeRec().getWidth() / 2.f;
742         outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
743     } else {
744         return false;
745     }
746     rect.outset(outset.fX, outset.fY);
747     if (rect.isEmpty()) {
748         this->changeType(Type::kEmpty);
749         fStyle = GrStyle::SimpleFill();
750         return true;
751     }
752     SkRRect rrect;
753     if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
754         SkASSERT(outset.fX == outset.fY);
755         rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY);
756     } else {
757         rrect = SkRRect::MakeRect(rect);
758     }
759     bool inverted = fLineData.fInverted && !fStyle.hasPathEffect();
760     this->changeType(Type::kRRect);
761     fRRectData.fRRect = rrect;
762     fRRectData.fInverted = inverted;
763     fRRectData.fDir = kDefaultRRectDir;
764     fRRectData.fStart = kDefaultRRectStart;
765     fStyle = GrStyle::SimpleFill();
766     return true;
767 }
768