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,SkPathDirection * dir,unsigned * start,bool * inverted) const437 bool GrStyledShape::asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start,
438 bool* inverted) const {
439 if (!fShape.isRRect() && !fShape.isRect()) {
440 return false;
441 }
442
443 // Validity check here, if we don't have a path effect on the style, we should have passed
444 // appropriate flags to GrShape::simplify() to have reset these parameters.
445 SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir &&
446 fShape.startIndex() == GrShape::kDefaultStart));
447
448 // If the shape is a regular rect, map to round rect winding parameters, including accounting
449 // for the automatic sorting of edges that SkRRect::MakeRect() performs.
450 if (fShape.isRect()) {
451 if (rrect) {
452 *rrect = SkRRect::MakeRect(fShape.rect());
453 }
454 // Don't bother mapping these if we don't have a path effect, however.
455 if (!fStyle.hasPathEffect()) {
456 if (dir) {
457 *dir = GrShape::kDefaultDir;
458 }
459 if (start) {
460 *start = GrShape::kDefaultStart;
461 }
462 } else {
463 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
464 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
465 // rect edges. Thus, we may need to modify the rrect's start index and direction.
466 SkPathDirection rectDir = fShape.dir();
467 unsigned rectStart = fShape.startIndex();
468
469 if (fShape.rect().fLeft > fShape.rect().fRight) {
470 // Toggle direction, and modify index by mapping through the array
471 static const unsigned kMapping[] = {1, 0, 3, 2};
472 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
473 : SkPathDirection::kCCW;
474 rectStart = kMapping[rectStart];
475 }
476 if (fShape.rect().fTop > fShape.rect().fBottom) {
477 // Toggle direction and map index by 3 - start
478 // NOTE: if we earlier flipped for X as well, this results in no net direction
479 // change and effectively flipping the start index to the diagonal corners of the
480 // rect (matching what we'd expect for a rect with both X and Y flipped).
481 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
482 : SkPathDirection::kCCW;
483 rectStart = 3 - rectStart;
484 }
485
486 if (dir) {
487 *dir = rectDir;
488 }
489 if (start) {
490 // Convert to round rect indexing
491 *start = 2 * rectStart;
492 }
493 }
494 } else {
495 // Straight forward export
496 if (rrect) {
497 *rrect = fShape.rrect();
498 }
499 if (dir) {
500 *dir = fShape.dir();
501 }
502 if (start) {
503 *start = fShape.startIndex();
504 // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special
505 // but we do for dashing placement
506 if (fShape.rrect().isOval()) {
507 *start &= 0b110;
508 }
509 }
510 }
511
512 if (inverted) {
513 *inverted = fShape.inverted();
514 }
515
516 return true;
517 }
518
asLine(SkPoint pts[2],bool * inverted) const519 bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
520 if (!fShape.isLine()) {
521 return false;
522 }
523
524 if (pts) {
525 pts[0] = fShape.line().fP1;
526 pts[1] = fShape.line().fP2;
527 }
528 if (inverted) {
529 *inverted = fShape.inverted();
530 }
531 return true;
532 }
533
asNestedRects(SkRect rects[2]) const534 bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
535 if (!fShape.isPath()) {
536 return false;
537 }
538
539 // TODO: it would be better two store DRRects natively in the shape rather than converting
540 // them to a path and then reextracting the nested rects
541 if (fShape.path().isInverseFillType()) {
542 return false;
543 }
544
545 SkPathDirection dirs[2];
546 if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
547 return false;
548 }
549
550 if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
551 // The two rects need to be wound opposite to each other
552 return false;
553 }
554
555 // Right now, nested rects where the margin is not the same width
556 // all around do not render correctly
557 const SkScalar* outer = rects[0].asScalars();
558 const SkScalar* inner = rects[1].asScalars();
559
560 bool allEq = true;
561
562 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
563 bool allGoE1 = margin >= SK_Scalar1;
564
565 for (int i = 1; i < 4; ++i) {
566 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
567 if (temp < SK_Scalar1) {
568 allGoE1 = false;
569 }
570 if (!SkScalarNearlyEqual(margin, temp)) {
571 allEq = false;
572 }
573 }
574
575 return allEq || allGoE1;
576 }
577
578 class AutoRestoreInverseness {
579 public:
AutoRestoreInverseness(GrShape * shape,const GrStyle & style)580 AutoRestoreInverseness(GrShape* shape, const GrStyle& style)
581 // Dashing ignores inverseness skbug.com/5421.
582 : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
583
~AutoRestoreInverseness()584 ~AutoRestoreInverseness() {
585 // Restore invertedness after any modifications were made to the shape type
586 fShape->setInverted(fInverted);
587 SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
588 }
589
590 private:
591 GrShape* fShape;
592 bool fInverted;
593 };
594
simplify()595 void GrStyledShape::simplify() {
596 AutoRestoreInverseness ari(&fShape, fStyle);
597
598 unsigned simplifyFlags = 0;
599 if (fStyle.isSimpleFill()) {
600 simplifyFlags = GrShape::kAll_Flags;
601 } else if (!fStyle.hasPathEffect()) {
602 // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
603 if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
604 simplifyFlags |= GrShape::kIgnoreWinding_Flag;
605 }
606 simplifyFlags |= GrShape::kMakeCanonical_Flag;
607 } // else if there's a path effect, every destructive simplification is disabledd
608
609 // Remember if the original shape was closed; in the event we simplify to a point or line
610 // because of degenerate geometry, we need to update joins and caps.
611 GrShape::Type oldType = fShape.type();
612 fClosed = fShape.simplify(simplifyFlags);
613 fSimplified = oldType != fShape.type();
614
615 if (fShape.isPath()) {
616 // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
617 if (fInheritedKey.count() || fShape.path().isVolatile()) {
618 fGenID = 0;
619 } else {
620 fGenID = fShape.path().getGenerationID();
621 }
622 if (!fStyle.hasNonDashPathEffect() &&
623 (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
624 fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style ||
625 fShape.path().isConvex())) {
626 // Stroke styles don't differentiate between winding and even/odd. There is no
627 // distinction between even/odd and non-zero winding count for convex paths.
628 // Moreover, dashing ignores inverseness (skbug.com/5421)
629 fShape.path().setFillType(GrShape::kDefaultFillType);
630 }
631 } else {
632 fInheritedKey.reset(0);
633 // Whenever we simplify to a non-path, break the chain so we no longer refer to the
634 // original path. This prevents attaching genID listeners to temporary paths created when
635 // drawing simple shapes.
636 fInheritedPathForListeners.reset();
637 // Further simplifications to the shape based on the style
638 this->simplifyStroke();
639 }
640 }
641
simplifyStroke()642 void GrStyledShape::simplifyStroke() {
643 AutoRestoreInverseness ari(&fShape, fStyle);
644
645 // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
646 // becomes a round rect.
647 if (!fStyle.hasPathEffect() && fShape.isRect() &&
648 fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
649 if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
650 (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
651 fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
652 // Bevel-stroked rect needs path rendering
653 return;
654 }
655
656 SkScalar r = fStyle.strokeRec().getWidth() / 2;
657 fShape.rect().outset(r, r);
658 if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
659 // There's no dashing to worry about if we got here, so it's okay that this resets
660 // winding parameters
661 fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
662 }
663 fStyle = GrStyle::SimpleFill();
664 fSimplified = true;
665 return;
666 }
667
668 // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
669 // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
670 if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
671 fStyle.strokeRec().isHairlineStyle()) {
672 return;
673 }
674
675 // Tracks style simplifications, even if the geometry can't be further simplified.
676 bool styleSimplified = false;
677 if (fStyle.isDashed()) {
678 // For dashing a point, if the first interval is on, we can drop the dash and just draw
679 // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
680 bool dropDash = false;
681 if (fShape.isPoint()) {
682 dropDash = fStyle.dashIntervalCnt() > 0 &&
683 SkToBool(fStyle.dashIntervals()[0]);
684 } else {
685 dropDash = true;
686 for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
687 if (SkToBool(fStyle.dashIntervals()[i])) {
688 // An off interval has non-zero length so this won't convert to a simple line
689 dropDash = false;
690 break;
691 }
692 }
693 }
694
695 if (!dropDash) {
696 return;
697 }
698 // Fall through to modifying the shape to respect the new stroke geometry
699 fStyle = GrStyle(fStyle.strokeRec(), nullptr);
700 // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
701 // we reset to be unclosed so we don't override the style based on joins later.
702 fClosed = false;
703 styleSimplified = true;
704 }
705
706 // At this point, we're a line or point with no path effects. Any fill portion of the style
707 // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
708 if (fStyle.isSimpleFill()) {
709 fShape.reset();
710 fSimplified = true;
711 return;
712 } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
713 // Stroke only
714 SkStrokeRec rec = fStyle.strokeRec();
715 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
716 fStyle = GrStyle(rec, nullptr);
717 styleSimplified = true;
718 }
719
720 // A point or line that was formed by a degenerate closed shape needs its style updated to
721 // reflect the fact that it doesn't actually produce caps.
722 if (fClosed) {
723 SkPaint::Cap cap;
724 if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
725 // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
726 // turn. With round joins, this would make a semi-circle at each end, which is visually
727 // identical to a round cap on the reduced line geometry.
728 cap = SkPaint::kRound_Cap;
729 } else {
730 // If this were a closed line, the 180 degree turn either is a miter join that exceeds
731 // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
732 // of a 180 degreen corner is equivalent to a butt cap.
733 // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
734 // it fits this closed line description (it is not two 90 degree turns that could
735 // produce miter geometry).
736 cap = SkPaint::kButt_Cap;
737 }
738
739 if (cap != fStyle.strokeRec().getCap() ||
740 SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) {
741 SkStrokeRec rec = fStyle.strokeRec();
742 rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter());
743 fStyle = GrStyle(rec, nullptr);
744 styleSimplified = true;
745 }
746 }
747
748 if (fShape.isPoint()) {
749 // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
750 // doesn't draw anything, a round cap is an oval and a square cap is a square.
751 if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
752 fShape.reset();
753 } else {
754 SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
755 SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
756 r.outset(w, w);
757
758 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
759 fShape.setRRect(SkRRect::MakeOval(r));
760 } else {
761 fShape.setRect(r);
762 }
763 }
764 } else {
765 // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
766 // allowed rotation angle, this would work for any lines.
767 SkRect rect;
768 SkVector outset;
769 if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
770 rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
771 rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
772 rect.fTop = rect.fBottom = fShape.line().fP1.fY;
773 outset.fY = fStyle.strokeRec().getWidth() / 2.f;
774 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
775 } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
776 rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
777 rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
778 rect.fLeft = rect.fRight = fShape.line().fP1.fX;
779 outset.fX = fStyle.strokeRec().getWidth() / 2.f;
780 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
781 } else {
782 // Geometrically can't apply the style and turn into a fill, but might still be simpler
783 // than before based solely on changes to fStyle.
784 fSimplified |= styleSimplified;
785 return;
786 }
787 rect.outset(outset.fX, outset.fY);
788 if (rect.isEmpty()) {
789 fShape.reset();
790 } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
791 SkASSERT(outset.fX == outset.fY);
792 fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
793 } else {
794 fShape.setRect(rect);
795 }
796 }
797 // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
798 fStyle = GrStyle::SimpleFill();
799 fSimplified = true;
800 }
801