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