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
14GrStyledShape& 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.get());
25 } else {
26 fInheritedPathForListeners.reset();
27 }
28 return *this;
29}
30
31static 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
45GrStyledShape 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.get());
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
81SkRect 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.
92static 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.
108static 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
133int 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
178void 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
241void 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
291const 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
300void GrStyledShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const {
301 if (const auto* lp = this->originalPathForListeners()) {
302 SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
303 }
304}
305
306GrStyledShape GrStyledShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
307 SkScalar sweepAngleDegrees, bool useCenter,
308 const GrStyle& style) {
309 GrStyledShape result;
310 result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter});
311 result.fStyle = style;
312 result.simplify();
313 return result;
314}
315
316GrStyledShape::GrStyledShape(const GrStyledShape& that)
317 : fShape(that.fShape)
318 , fStyle(that.fStyle)
319 , fGenID(that.fGenID)
320 , fSimplified(that.fSimplified) {
321 fInheritedKey.reset(that.fInheritedKey.count());
322 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
323 sizeof(uint32_t) * fInheritedKey.count());
324 if (that.fInheritedPathForListeners.isValid()) {
325 fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
326 }
327}
328
329GrStyledShape::GrStyledShape(const GrStyledShape& parent, GrStyle::Apply apply, SkScalar scale) {
330 // TODO: Add some quantization of scale for better cache performance here or leave that up
331 // to caller?
332 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
333 // stroke of a rect).
334 if (!parent.style().applies() ||
335 (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
336 *this = parent;
337 return;
338 }
339
340 SkPathEffect* pe = parent.fStyle.pathEffect();
341 SkTLazy<SkPath> tmpPath;
342 const GrStyledShape* parentForKey = &parent;
343 SkTLazy<GrStyledShape> tmpParent;
344
345 // Start out as an empty path that is filled in by the applied style
346 fShape.setPath(SkPath());
347
348 if (pe) {
349 const SkPath* srcForPathEffect;
350 if (parent.fShape.isPath()) {
351 srcForPathEffect = &parent.fShape.path();
352 } else {
353 srcForPathEffect = tmpPath.init();
354 parent.asPath(tmpPath.get());
355 }
356 // Should we consider bounds? Would have to include in key, but it'd be nice to know
357 // if the bounds actually modified anything before including in key.
358 SkStrokeRec strokeRec = parent.fStyle.strokeRec();
359 if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect,
360 scale)) {
361 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
362 *this = tmpParent.get()->applyStyle(apply, scale);
363 return;
364 }
365 // A path effect has access to change the res scale but we aren't expecting it to and it
366 // would mess up our key computation.
367 SkASSERT(scale == strokeRec.getResScale());
368 if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
369 // The intermediate shape may not be a general path. If we we're just applying
370 // the path effect then attemptToReduceFromPath would catch it. This means that
371 // when we subsequently applied the remaining strokeRec we would have a non-path
372 // parent shape that would be used to determine the the stroked path's key.
373 // We detect that case here and change parentForKey to a temporary that represents
374 // the simpler shape so that applying both path effect and the strokerec all at
375 // once produces the same key.
376 tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr));
377 tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
378 if (!tmpPath.isValid()) {
379 tmpPath.init();
380 }
381 tmpParent.get()->asPath(tmpPath.get());
382 SkStrokeRec::InitStyle fillOrHairline;
383 // The parent shape may have simplified away the strokeRec, check for that here.
384 if (tmpParent.get()->style().applies()) {
385 SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline,
386 *tmpPath.get(), scale));
387 } else if (tmpParent.get()->style().isSimpleFill()) {
388 fillOrHairline = SkStrokeRec::kFill_InitStyle;
389 } else {
390 SkASSERT(tmpParent.get()->style().isSimpleHairline());
391 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
392 }
393 fStyle.resetToInitStyle(fillOrHairline);
394 parentForKey = tmpParent.get();
395 } else {
396 fStyle = GrStyle(strokeRec, nullptr);
397 }
398 } else {
399 const SkPath* srcForParentStyle;
400 if (parent.fShape.isPath()) {
401 srcForParentStyle = &parent.fShape.path();
402 } else {
403 srcForParentStyle = tmpPath.init();
404 parent.asPath(tmpPath.get());
405 }
406 SkStrokeRec::InitStyle fillOrHairline;
407 SkASSERT(parent.fStyle.applies());
408 SkASSERT(!parent.fStyle.pathEffect());
409 SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline,
410 *srcForParentStyle, scale));
411 fStyle.resetToInitStyle(fillOrHairline);
412 }
413
414 if (parent.fInheritedPathForListeners.isValid()) {
415 fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get());
416 } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) {
417 fInheritedPathForListeners.set(parent.fShape.path());
418 }
419 this->simplify();
420 this->setInheritedKey(*parentForKey, apply, scale);
421}
422
423bool GrStyledShape::asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start,
424 bool* inverted) const {
425 if (!fShape.isRRect() && !fShape.isRect()) {
426 return false;
427 }
428
429 // Validity check here, if we don't have a path effect on the style, we should have passed
430 // appropriate flags to GrShape::simplify() to have reset these parameters.
431 SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir &&
432 fShape.startIndex() == GrShape::kDefaultStart));
433
434 // If the shape is a regular rect, map to round rect winding parameters, including accounting
435 // for the automatic sorting of edges that SkRRect::MakeRect() performs.
436 if (fShape.isRect()) {
437 if (rrect) {
438 *rrect = SkRRect::MakeRect(fShape.rect());
439 }
440 // Don't bother mapping these if we don't have a path effect, however.
441 if (!fStyle.hasPathEffect()) {
442 if (dir) {
443 *dir = GrShape::kDefaultDir;
444 }
445 if (start) {
446 *start = GrShape::kDefaultStart;
447 }
448 } else {
449 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
450 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
451 // rect edges. Thus, we may need to modify the rrect's start index and direction.
452 SkPathDirection rectDir = fShape.dir();
453 unsigned rectStart = fShape.startIndex();
454
455 if (fShape.rect().fLeft > fShape.rect().fRight) {
456 // Toggle direction, and modify index by mapping through the array
457 static const unsigned kMapping[] = {1, 0, 3, 2};
458 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
459 : SkPathDirection::kCCW;
460 rectStart = kMapping[rectStart];
461 }
462 if (fShape.rect().fTop > fShape.rect().fBottom) {
463 // Toggle direction and map index by 3 - start
464 // NOTE: if we earlier flipped for X as well, this results in no net direction
465 // change and effectively flipping the start index to the diagonal corners of the
466 // rect (matching what we'd expect for a rect with both X and Y flipped).
467 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
468 : SkPathDirection::kCCW;
469 rectStart = 3 - rectStart;
470 }
471
472 if (dir) {
473 *dir = rectDir;
474 }
475 if (start) {
476 // Convert to round rect indexing
477 *start = 2 * rectStart;
478 }
479 }
480 } else {
481 // Straight forward export
482 if (rrect) {
483 *rrect = fShape.rrect();
484 }
485 if (dir) {
486 *dir = fShape.dir();
487 }
488 if (start) {
489 *start = fShape.startIndex();
490 // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special
491 // but we do for dashing placement
492 if (fShape.rrect().isOval()) {
493 *start &= 0b110;
494 }
495 }
496 }
497
498 if (inverted) {
499 *inverted = fShape.inverted();
500 }
501
502 return true;
503}
504
505bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
506 if (!fShape.isLine()) {
507 return false;
508 }
509
510 if (pts) {
511 pts[0] = fShape.line().fP1;
512 pts[1] = fShape.line().fP2;
513 }
514 if (inverted) {
515 *inverted = fShape.inverted();
516 }
517 return true;
518}
519
520bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
521 if (!fShape.isPath()) {
522 return false;
523 }
524
525 // TODO: it would be better two store DRRects natively in the shape rather than converting
526 // them to a path and then reextracting the nested rects
527 if (fShape.path().isInverseFillType()) {
528 return false;
529 }
530
531 SkPathDirection dirs[2];
532 if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
533 return false;
534 }
535
536 if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
537 // The two rects need to be wound opposite to each other
538 return false;
539 }
540
541 // Right now, nested rects where the margin is not the same width
542 // all around do not render correctly
543 const SkScalar* outer = rects[0].asScalars();
544 const SkScalar* inner = rects[1].asScalars();
545
546 bool allEq = true;
547
548 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
549 bool allGoE1 = margin >= SK_Scalar1;
550
551 for (int i = 1; i < 4; ++i) {
552 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
553 if (temp < SK_Scalar1) {
554 allGoE1 = false;
555 }
556 if (!SkScalarNearlyEqual(margin, temp)) {
557 allEq = false;
558 }
559 }
560
561 return allEq || allGoE1;
562}
563
564void GrStyledShape::simplify() {
565 // Dashing ignores inverseness skbug.com/5421.
566 bool inverted = !fStyle.isDashed() && fShape.inverted();
567
568 unsigned simplifyFlags = 0;
569 if (fStyle.isSimpleFill()) {
570 simplifyFlags = GrShape::kAll_Flags;
571 } else if (!fStyle.hasPathEffect()) {
572 // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
573 if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
574 simplifyFlags |= GrShape::kIgnoreWinding_Flag;
575 }
576 simplifyFlags |= GrShape::kMakeCanonical_Flag;
577 } // else if there's a path effect, every destructive simplification is disabledd
578
579 // Remember if the original shape was closed; in the event we simplify to a point or line
580 // because of degenerate geometry, we need to update joins and caps.
581 GrShape::Type oldType = fShape.type();
582 bool wasClosed = fShape.simplify(simplifyFlags);
583 fSimplified = oldType != fShape.type();
584
585 if (fShape.isPath()) {
586 // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
587 if (fInheritedKey.count() || fShape.path().isVolatile()) {
588 fGenID = 0;
589 } else {
590 fGenID = fShape.path().getGenerationID();
591 }
592 if (!fStyle.hasNonDashPathEffect() &&
593 (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
594 fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style ||
595 fShape.path().isConvex())) {
596 // Stroke styles don't differentiate between winding and even/odd. There is no
597 // distinction between even/odd and non-zero winding count for convex paths.
598 // Moreover, dashing ignores inverseness (skbug.com/5421)
599 fShape.path().setFillType(GrShape::kDefaultFillType);
600 }
601 } else {
602 fInheritedKey.reset(0);
603 // Whenever we simplify to a non-path, break the chain so we no longer refer to the
604 // original path. This prevents attaching genID listeners to temporary paths created when
605 // drawing simple shapes.
606 fInheritedPathForListeners.reset();
607
608 // Further simplifications to the shape based on the style
609 fSimplified |= this->simplifyStroke(wasClosed);
610 }
611
612 // Restore invertedness after any modifications were made to the shape type
613 fShape.setInverted(inverted);
614 SkASSERT(!fShape.isPath() || inverted == fShape.path().isInverseFillType());
615}
616
617bool GrStyledShape::simplifyStroke(bool originallyClosed) {
618 // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
619 // becomes a round rect.
620 if (!fStyle.hasPathEffect() && fShape.isRect() &&
621 fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
622 if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
623 (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
624 fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
625 // Bevel-stroked rect needs path rendering
626 return false;
627 }
628
629 SkScalar r = fStyle.strokeRec().getWidth() / 2;
630 fShape.rect().outset(r, r);
631 if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
632 // There's no dashing to worry about if we got here, so it's okay that this resets
633 // winding parameters
634 fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
635 }
636 fStyle = GrStyle::SimpleFill();
637 return true;
638 }
639
640 // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
641 // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
642 if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
643 fStyle.strokeRec().isHairlineStyle()) {
644 return false;
645 }
646
647 // Tracks style simplifications, even if the geometry can't be further simplified.
648 bool styleSimplified = false;
649 if (fStyle.isDashed()) {
650 // For dashing a point, if the first interval is on, we can drop the dash and just draw
651 // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
652 bool dropDash = false;
653 if (fShape.isPoint()) {
654 dropDash = fStyle.dashIntervalCnt() > 0 &&
655 SkToBool(fStyle.dashIntervals()[0]);
656 } else {
657 dropDash = true;
658 for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
659 if (SkToBool(fStyle.dashIntervals()[i])) {
660 // An off interval has non-zero length so this won't convert to a simple line
661 dropDash = false;
662 break;
663 }
664 }
665 }
666
667 if (!dropDash) {
668 return false;
669 }
670 // Fall through to modifying the shape to respect the new stroke geometry
671 fStyle = GrStyle(fStyle.strokeRec(), nullptr);
672 // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
673 // we reset to be unclosed so we don't override the style based on joins later.
674 originallyClosed = false;
675 styleSimplified = true;
676 }
677
678 // At this point, we're a line or point with no path effects. Any fill portion of the style
679 // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
680 bool strokeAndFilled = false;
681 if (fStyle.isSimpleFill()) {
682 fShape.reset();
683 return true;
684 } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
685 // Stroke only
686 SkStrokeRec rec = fStyle.strokeRec();
687 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
688 fStyle = GrStyle(rec, nullptr);
689 styleSimplified = true;
690 strokeAndFilled = true;
691 }
692
693 // A point or line that was formed by a degenerate closed shape needs its style updated to
694 // reflect the fact that it doesn't actually produce caps.
695 if (originallyClosed) {
696 SkPaint::Cap cap;
697 if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
698 // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
699 // turn. With round joins, this would make a semi-circle at each end, which is visually
700 // identical to a round cap on the reduced line geometry.
701 cap = SkPaint::kRound_Cap;
702 } else if (fShape.isPoint() && fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
703 !strokeAndFilled) {
704 // Use a square cap for miter join + stroked points, which matches raster's behavior and
705 // expectations from Chrome masking tests, although it could be argued to just always
706 // use a butt cap. This behavior, though, ensures that the default stroked paint draws
707 // something with empty geometry.
708 cap = SkPaint::kSquare_Cap;
709 } else {
710 // If this were a closed line, the 180 degree turn either is a miter join that exceeds
711 // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
712 // of a 180 degreen corner is equivalent to a butt cap.
713 // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
714 // it fits this closed line description (it is not two 90 degree turns that could
715 // produce miter geometry).
716 cap = SkPaint::kButt_Cap;
717 }
718
719 if (cap != fStyle.strokeRec().getCap() ||
720 SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) {
721 SkStrokeRec rec = fStyle.strokeRec();
722 rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter());
723 fStyle = GrStyle(rec, nullptr);
724 styleSimplified = true;
725 }
726 }
727
728 if (fShape.isPoint()) {
729 // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
730 // doesn't draw anything, a round cap is an oval and a square cap is a square.
731 if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
732 fShape.reset();
733 } else {
734 SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
735 SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
736 r.outset(w, w);
737
738 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
739 fShape.setRRect(SkRRect::MakeOval(r));
740 } else {
741 fShape.setRect(r);
742 }
743 }
744 } else {
745 // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
746 // allowed rotation angle, this would work for any lines.
747 SkRect rect;
748 SkVector outset;
749 if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
750 rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
751 rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
752 rect.fTop = rect.fBottom = fShape.line().fP1.fY;
753 outset.fY = fStyle.strokeRec().getWidth() / 2.f;
754 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
755 } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
756 rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
757 rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
758 rect.fLeft = rect.fRight = fShape.line().fP1.fX;
759 outset.fX = fStyle.strokeRec().getWidth() / 2.f;
760 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
761 } else {
762 // Geometrically can't apply the style and turn into a fill, but might still be simpler
763 // than before based solely on changes to fStyle.
764 return styleSimplified;
765 }
766 rect.outset(outset.fX, outset.fY);
767 if (rect.isEmpty()) {
768 fShape.reset();
769 } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
770 SkASSERT(outset.fX == outset.fY);
771 fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
772 } else {
773 fShape.setRect(rect);
774 }
775 }
776 // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
777 fStyle = GrStyle::SimpleFill();
778 return true;
779}
780