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