Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
GrStyledShape.cpp
Go to the documentation of this file.
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 */
8
19
20#include <algorithm>
21#include <cstring>
22#include <utility>
23
24
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
42static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion) {
43 switch (inversion) {
45 return originalIsInverted;
47 return !originalIsInverted;
49 return true;
51 return false;
52 }
53 return false;
54}
55
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 }
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
93 if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
94 return SkRect::MakeEmpty();
95 }
96
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.
103static int path_key_from_data_size(const SkPath& path) {
104 const int verbCnt = path.countVerbs();
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.
119static 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);
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
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()) {
152 static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t));
153 count += sizeof(SkPoint) / sizeof(uint32_t);
154 break;
156 static_assert(0 == sizeof(SkRect) % sizeof(uint32_t));
157 count += sizeof(SkRect) / sizeof(uint32_t);
158 break;
160 static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
161 count += SkRRect::kSizeInMemory / sizeof(uint32_t);
162 break;
164 static_assert(0 == sizeof(SkArc) % sizeof(uint32_t));
165 count += sizeof(SkArc) / sizeof(uint32_t);
166 break;
168 static_assert(0 == sizeof(GrLineSegment) % sizeof(uint32_t));
169 count += sizeof(GrLineSegment) / sizeof(uint32_t);
170 break;
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
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 &&
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()) {
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) {
216 return;
217 } else {
218 *key++ = fGenID;
219 }
220 break; }
222 memcpy(key, &fShape.point(), sizeof(SkPoint));
223 key += sizeof(SkPoint) / sizeof(uint32_t);
224 break;
226 memcpy(key, &fShape.rect(), sizeof(SkRect));
227 key += sizeof(SkRect) / sizeof(uint32_t);
228 break;
230 fShape.rrect().writeToMemory(key);
231 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
232 break;
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().fUseCenter ? 1 : 0;
239 break;
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
252void 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
302const 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
312 if (const auto* lp = this->originalPathForListeners()) {
313 SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
314 }
315}
316
318 SkScalar sweepAngleDegrees, bool useCenter,
319 const GrStyle& style, DoSimplify doSimplify) {
321 result.fShape.setArc({oval.makeSorted(), startAngleDegrees, sweepAngleDegrees, useCenter});
322 result.fStyle = style;
323 if (doSimplify == DoSimplify::kYes) {
324 result.simplify();
325 }
326 return result;
327}
328
330 : fShape(that.fShape)
331 , fStyle(that.fStyle)
332 , fGenID(that.fGenID)
333 , fSimplified(that.fSimplified) {
334 fInheritedKey.reset(that.fInheritedKey.count());
335 sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
336 sizeof(uint32_t) * fInheritedKey.count());
337 if (that.fInheritedPathForListeners.isValid()) {
338 fInheritedPathForListeners.set(*that.fInheritedPathForListeners);
339 }
340}
341
343 // TODO: Add some quantization of scale for better cache performance here or leave that up
344 // to caller?
345 // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
346 // stroke of a rect).
347 if (!parent.style().applies() ||
349 *this = parent;
350 return;
351 }
352
353 SkPathEffect* pe = parent.fStyle.pathEffect();
354 SkTLazy<SkPath> tmpPath;
355 const GrStyledShape* parentForKey = &parent;
356 SkTLazy<GrStyledShape> tmpParent;
357
358 // Start out as an empty path that is filled in by the applied style
359 fShape.setPath(SkPath());
360
361 if (pe) {
362 const SkPath* srcForPathEffect;
363 if (parent.fShape.isPath()) {
364 srcForPathEffect = &parent.fShape.path();
365 } else {
366 srcForPathEffect = tmpPath.init();
367 parent.asPath(tmpPath.get());
368 }
369 // Should we consider bounds? Would have to include in key, but it'd be nice to know
370 // if the bounds actually modified anything before including in key.
371 SkStrokeRec strokeRec = parent.fStyle.strokeRec();
372 if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect,
373 scale)) {
374 tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
375 *this = tmpParent->applyStyle(apply, scale);
376 return;
377 }
378 // A path effect has access to change the res scale but we aren't expecting it to and it
379 // would mess up our key computation.
380 SkASSERT(scale == strokeRec.getResScale());
382 // The intermediate shape may not be a general path. If we we're just applying
383 // the path effect then attemptToReduceFromPath would catch it. This means that
384 // when we subsequently applied the remaining strokeRec we would have a non-path
385 // parent shape that would be used to determine the the stroked path's key.
386 // We detect that case here and change parentForKey to a temporary that represents
387 // the simpler shape so that applying both path effect and the strokerec all at
388 // once produces the same key.
389 tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr));
390 tmpParent->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
391 if (!tmpPath.isValid()) {
392 tmpPath.init();
393 }
394 tmpParent->asPath(tmpPath.get());
395 SkStrokeRec::InitStyle fillOrHairline;
396 // The parent shape may have simplified away the strokeRec, check for that here.
397 if (tmpParent->style().applies()) {
398 SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline,
399 *tmpPath.get(), scale));
400 } else if (tmpParent->style().isSimpleFill()) {
401 fillOrHairline = SkStrokeRec::kFill_InitStyle;
402 } else {
403 SkASSERT(tmpParent.get()->style().isSimpleHairline());
404 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
405 }
406 fStyle.resetToInitStyle(fillOrHairline);
407 parentForKey = tmpParent.get();
408 } else {
409 fStyle = GrStyle(strokeRec, nullptr);
410 }
411 } else {
412 const SkPath* srcForParentStyle;
413 if (parent.fShape.isPath()) {
414 srcForParentStyle = &parent.fShape.path();
415 } else {
416 srcForParentStyle = tmpPath.init();
417 parent.asPath(tmpPath.get());
418 }
419 SkStrokeRec::InitStyle fillOrHairline;
420 SkASSERT(parent.fStyle.applies());
421 SkASSERT(!parent.fStyle.pathEffect());
422 SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline,
423 *srcForParentStyle, scale));
424 fStyle.resetToInitStyle(fillOrHairline);
425 }
426
427 if (parent.fInheritedPathForListeners.isValid()) {
428 fInheritedPathForListeners.set(*parent.fInheritedPathForListeners);
429 } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) {
430 fInheritedPathForListeners.set(parent.fShape.path());
431 }
432 this->simplify();
433 this->setInheritedKey(*parentForKey, apply, scale);
434}
435
437 bool* inverted) const {
438 if (!fShape.isRRect() && !fShape.isRect()) {
439 return false;
440 }
441
442 // Validity check here, if we don't have a path effect on the style, we should have passed
443 // appropriate flags to GrShape::simplify() to have reset these parameters.
444 SkASSERT(fStyle.hasPathEffect() || (fShape.dir() == GrShape::kDefaultDir &&
446
447 // If the shape is a regular rect, map to round rect winding parameters, including accounting
448 // for the automatic sorting of edges that SkRRect::MakeRect() performs.
449 if (fShape.isRect()) {
450 if (rrect) {
451 *rrect = SkRRect::MakeRect(fShape.rect());
452 }
453 // Don't bother mapping these if we don't have a path effect, however.
454 if (!fStyle.hasPathEffect()) {
455 if (dir) {
457 }
458 if (start) {
460 }
461 } else {
462 // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
463 // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
464 // rect edges. Thus, we may need to modify the rrect's start index and direction.
465 SkPathDirection rectDir = fShape.dir();
466 unsigned rectStart = fShape.startIndex();
467
468 if (fShape.rect().fLeft > fShape.rect().fRight) {
469 // Toggle direction, and modify index by mapping through the array
470 static const unsigned kMapping[] = {1, 0, 3, 2};
471 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
473 rectStart = kMapping[rectStart];
474 }
475 if (fShape.rect().fTop > fShape.rect().fBottom) {
476 // Toggle direction and map index by 3 - start
477 // NOTE: if we earlier flipped for X as well, this results in no net direction
478 // change and effectively flipping the start index to the diagonal corners of the
479 // rect (matching what we'd expect for a rect with both X and Y flipped).
480 rectDir = rectDir == SkPathDirection::kCCW ? SkPathDirection::kCW
482 rectStart = 3 - rectStart;
483 }
484
485 if (dir) {
486 *dir = rectDir;
487 }
488 if (start) {
489 // Convert to round rect indexing
490 *start = 2 * rectStart;
491 }
492 }
493 } else {
494 // Straight forward export
495 if (rrect) {
496 *rrect = fShape.rrect();
497 }
498 if (dir) {
499 *dir = fShape.dir();
500 }
501 if (start) {
502 *start = fShape.startIndex();
503 // Canonicalize the index if the rrect is an oval, which GrShape doesn't treat special
504 // but we do for dashing placement
505 if (fShape.rrect().isOval()) {
506 *start &= 0b110;
507 }
508 }
509 }
510
511 if (inverted) {
512 *inverted = fShape.inverted();
513 }
514
515 return true;
516}
517
518bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
519 if (!fShape.isLine()) {
520 return false;
521 }
522
523 if (pts) {
524 pts[0] = fShape.line().fP1;
525 pts[1] = fShape.line().fP2;
526 }
527 if (inverted) {
528 *inverted = fShape.inverted();
529 }
530 return true;
531}
532
534 if (!fShape.isPath()) {
535 return false;
536 }
537
538 // TODO: it would be better two store DRRects natively in the shape rather than converting
539 // them to a path and then reextracting the nested rects
540 if (fShape.path().isInverseFillType()) {
541 return false;
542 }
543
544 SkPathDirection dirs[2];
545 if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
546 return false;
547 }
548
549 if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
550 // The two rects need to be wound opposite to each other
551 return false;
552 }
553
554 // Right now, nested rects where the margin is not the same width
555 // all around do not render correctly
556 const SkScalar* outer = rects[0].asScalars();
557 const SkScalar* inner = rects[1].asScalars();
558
559 bool allEq = true;
560
561 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
562 bool allGoE1 = margin >= SK_Scalar1;
563
564 for (int i = 1; i < 4; ++i) {
565 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
566 if (temp < SK_Scalar1) {
567 allGoE1 = false;
568 }
569 if (!SkScalarNearlyEqual(margin, temp)) {
570 allEq = false;
571 }
572 }
573
574 return allEq || allGoE1;
575}
576
578public:
580 // Dashing ignores inverseness skbug.com/5421.
581 : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
582
584 // Restore invertedness after any modifications were made to the shape type
585 fShape->setInverted(fInverted);
586 SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
587 }
588
589private:
590 GrShape* fShape;
591 bool fInverted;
592};
593
595 AutoRestoreInverseness ari(&fShape, fStyle);
596
597 unsigned simplifyFlags = 0;
598 if (fStyle.isSimpleFill()) {
599 simplifyFlags = GrShape::kAll_Flags;
600 } else if (!fStyle.hasPathEffect()) {
601 // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
602 if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
603 simplifyFlags |= GrShape::kIgnoreWinding_Flag;
604 }
605 simplifyFlags |= GrShape::kMakeCanonical_Flag;
606 } // else if there's a path effect, every destructive simplification is disabledd
607
608 // Remember if the original shape was closed; in the event we simplify to a point or line
609 // because of degenerate geometry, we need to update joins and caps.
610 GrShape::Type oldType = fShape.type();
611 fClosed = fShape.simplify(simplifyFlags);
612 fSimplified = oldType != fShape.type();
613
614 if (fShape.isPath()) {
615 // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
616 if (fInheritedKey.count() || fShape.path().isVolatile()) {
617 fGenID = 0;
618 } else {
619 fGenID = fShape.path().getGenerationID();
620 }
621 if (!fStyle.hasNonDashPathEffect() &&
624 fShape.path().isConvex())) {
625 // Stroke styles don't differentiate between winding and even/odd. There is no
626 // distinction between even/odd and non-zero winding count for convex paths.
627 // Moreover, dashing ignores inverseness (skbug.com/5421)
629 }
630 } else {
631 fInheritedKey.reset(0);
632 // Whenever we simplify to a non-path, break the chain so we no longer refer to the
633 // original path. This prevents attaching genID listeners to temporary paths created when
634 // drawing simple shapes.
635 fInheritedPathForListeners.reset();
636 // Further simplifications to the shape based on the style
637 this->simplifyStroke();
638 }
639}
640
641void GrStyledShape::simplifyStroke() {
642 AutoRestoreInverseness ari(&fShape, fStyle);
643
644 // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
645 // becomes a round rect.
646 if (!fStyle.hasPathEffect() && fShape.isRect() &&
648 if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
649 (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
650 fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
651 // Bevel-stroked rect needs path rendering
652 return;
653 }
654
655 SkScalar r = fStyle.strokeRec().getWidth() / 2;
656 fShape.rect().outset(r, r);
657 if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
658 // There's no dashing to worry about if we got here, so it's okay that this resets
659 // winding parameters
660 fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
661 }
662 fStyle = GrStyle::SimpleFill();
663 fSimplified = true;
664 return;
665 }
666
667 // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
668 // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
669 if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
670 fStyle.strokeRec().isHairlineStyle()) {
671 return;
672 }
673
674 // Tracks style simplifications, even if the geometry can't be further simplified.
675 bool styleSimplified = false;
676 if (fStyle.isDashed()) {
677 // For dashing a point, if the first interval is on, we can drop the dash and just draw
678 // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
679 bool dropDash = false;
680 if (fShape.isPoint()) {
681 dropDash = fStyle.dashIntervalCnt() > 0 &&
682 SkToBool(fStyle.dashIntervals()[0]);
683 } else {
684 dropDash = true;
685 for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
686 if (SkToBool(fStyle.dashIntervals()[i])) {
687 // An off interval has non-zero length so this won't convert to a simple line
688 dropDash = false;
689 break;
690 }
691 }
692 }
693
694 if (!dropDash) {
695 return;
696 }
697 // Fall through to modifying the shape to respect the new stroke geometry
698 fStyle = GrStyle(fStyle.strokeRec(), nullptr);
699 // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
700 // we reset to be unclosed so we don't override the style based on joins later.
701 fClosed = false;
702 styleSimplified = true;
703 }
704
705 // At this point, we're a line or point with no path effects. Any fill portion of the style
706 // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
707 if (fStyle.isSimpleFill()) {
708 fShape.reset();
709 fSimplified = true;
710 return;
711 } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
712 // Stroke only
713 SkStrokeRec rec = fStyle.strokeRec();
714 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
715 fStyle = GrStyle(rec, nullptr);
716 styleSimplified = true;
717 }
718
719 // A point or line that was formed by a degenerate closed shape needs its style updated to
720 // reflect the fact that it doesn't actually produce caps.
721 if (fClosed) {
722 SkPaint::Cap cap;
723 if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
724 // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
725 // turn. With round joins, this would make a semi-circle at each end, which is visually
726 // identical to a round cap on the reduced line geometry.
728 } else {
729 // If this were a closed line, the 180 degree turn either is a miter join that exceeds
730 // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
731 // of a 180 degreen corner is equivalent to a butt cap.
732 // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
733 // it fits this closed line description (it is not two 90 degree turns that could
734 // produce miter geometry).
735 cap = SkPaint::kButt_Cap;
736 }
737
738 if (cap != fStyle.strokeRec().getCap() ||
740 SkStrokeRec rec = fStyle.strokeRec();
742 fStyle = GrStyle(rec, nullptr);
743 styleSimplified = true;
744 }
745 }
746
747 if (fShape.isPoint()) {
748 // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
749 // doesn't draw anything, a round cap is an oval and a square cap is a square.
750 if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
751 fShape.reset();
752 } else {
753 SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
754 SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
755 r.outset(w, w);
756
757 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
758 fShape.setRRect(SkRRect::MakeOval(r));
759 } else {
760 fShape.setRect(r);
761 }
762 }
763 } else {
764 // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
765 // allowed rotation angle, this would work for any lines.
766 SkRect rect;
768 if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
769 rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
770 rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
771 rect.fTop = rect.fBottom = fShape.line().fP1.fY;
772 outset.fY = fStyle.strokeRec().getWidth() / 2.f;
773 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
774 } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
775 rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
776 rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
777 rect.fLeft = rect.fRight = fShape.line().fP1.fX;
778 outset.fX = fStyle.strokeRec().getWidth() / 2.f;
779 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
780 } else {
781 // Geometrically can't apply the style and turn into a fill, but might still be simpler
782 // than before based solely on changes to fStyle.
783 fSimplified |= styleSimplified;
784 return;
785 }
786 rect.outset(outset.fX, outset.fY);
787 if (rect.isEmpty()) {
788 fShape.reset();
789 } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
790 SkASSERT(outset.fX == outset.fY);
791 fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
792 } else {
793 fShape.setRect(rect);
794 }
795 }
796 // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
797 fStyle = GrStyle::SimpleFill();
798 fSimplified = true;
799}
SkStrokeRec::Style fStyle
static const int outset
Definition BlurTest.cpp:58
int count
static bool inversion(Vertex *prev, Vertex *next, Edge *origEdge, const Comparator &c)
static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion)
static int path_key_from_data_size(const SkPath &path)
static void write_path_key_from_data(const SkPath &path, uint32_t *origKey)
static constexpr T SkAlign4(T x)
Definition SkAlign.h:16
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static void * sk_careful_memcpy(void *dst, const void *src, size_t len)
Definition SkMalloc.h:125
SkPathDirection
Definition SkPathTypes.h:34
static bool apply(Pass *pass, SkRecord *record)
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:107
#define SK_Scalar1
Definition SkScalar.h:18
#define SK_ScalarSqrt2
Definition SkScalar.h:20
#define SkScalarAbs(x)
Definition SkScalar.h:39
static bool is_inverted(const SkRect &r)
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
AutoRestoreInverseness(GrShape *shape, const GrStyle &style)
SkRect & rect()
Definition GrShape.h:134
Type type() const
Definition GrShape.h:92
uint32_t stateKey() const
Definition GrShape.cpp:48
bool isPath() const
Definition GrShape.h:88
void setRRect(const SkRRect &rrect)
Definition GrShape.h:163
void reset()
Definition GrShape.h:188
bool isRRect() const
Definition GrShape.h:87
SkPath & path()
Definition GrShape.h:140
bool isLine() const
Definition GrShape.h:90
SkRRect & rrect()
Definition GrShape.h:137
bool isRect() const
Definition GrShape.h:86
GrLineSegment & line()
Definition GrShape.h:146
void setPath(const SkPath &path)
Definition GrShape.h:175
@ kMakeCanonical_Flag
Definition GrShape.h:203
@ kIgnoreWinding_Flag
Definition GrShape.h:200
@ kAll_Flags
Definition GrShape.h:205
bool isPoint() const
Definition GrShape.h:85
SkArc & arc()
Definition GrShape.h:143
bool inverted() const
Definition GrShape.h:99
SkPathDirection dir() const
Definition GrShape.h:105
static constexpr SkPathDirection kDefaultDir
Definition GrShape.h:60
static constexpr SkPathFillType kDefaultFillType
Definition GrShape.h:63
bool isArc() const
Definition GrShape.h:89
void setInverted(bool inverted)
Definition GrShape.h:119
void setArc(const SkArc &arc)
Definition GrShape.h:167
unsigned startIndex() const
Definition GrShape.h:108
static constexpr unsigned kDefaultStart
Definition GrShape.h:61
SkPoint & point()
Definition GrShape.h:131
bool isEmpty() const
Definition GrShape.h:84
void setRect(const SkRect &rect)
Definition GrShape.h:159
bool simplify(unsigned flags=kAll_Flags)
Definition GrShape.cpp:242
bool isDashed() const
Definition GrStyle.h:126
@ kNoJoins_KeyFlag
Definition GrStyle.h:57
@ kClosed_KeyFlag
Definition GrStyle.h:55
void adjustBounds(SkRect *dst, const SkRect &src) const
Definition GrStyle.h:173
const SkScalar * dashIntervals() const
Definition GrStyle.h:135
bool applyToPath(SkPath *dst, SkStrokeRec::InitStyle *fillOrHairline, const SkPath &src, SkScalar scale) const
Definition GrStyle.cpp:175
SkPathEffect * pathEffect() const
Definition GrStyle.h:119
bool hasPathEffect() const
Definition GrStyle.h:122
static void WriteKey(uint32_t *, const GrStyle &, Apply, SkScalar scale, uint32_t flags=0)
Definition GrStyle.cpp:33
static const GrStyle & SimpleFill()
Definition GrStyle.h:30
bool hasNonDashPathEffect() const
Definition GrStyle.h:124
bool applies() const
Definition GrStyle.h:143
int dashIntervalCnt() const
Definition GrStyle.h:131
bool applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStoke, const SkPath &src, SkScalar scale) const
Definition GrStyle.cpp:163
bool isSimpleFill() const
Definition GrStyle.h:114
void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline)
Definition GrStyle.h:103
static int KeySize(const GrStyle &, Apply, uint32_t flags=0)
Definition GrStyle.cpp:11
const SkStrokeRec & strokeRec() const
Definition GrStyle.h:140
bool knownToBeClosed() const
void asPath(SkPath *out) const
void writeUnstyledKey(uint32_t *key) const
bool isEmpty() const
int unstyledKeySize() const
bool asLine(SkPoint pts[2], bool *inverted) const
const GrStyle & style() const
static GrStyledShape MakeArc(const SkRect &oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees, bool useCenter, const GrStyle &style, DoSimplify=DoSimplify::kYes)
SkRect styledBounds() const
SkRect bounds() const
bool asNestedRects(SkRect rects[2]) const
void addGenIDChangeListener(sk_sp< SkIDChangeListener >) const
static constexpr int kMaxKeyFromDataVerbCnt
static GrStyledShape MakeFilled(const GrStyledShape &original, FillInversion=FillInversion::kPreserve)
GrStyledShape & operator=(const GrStyledShape &that)
bool asRRect(SkRRect *rrect, SkPathDirection *dir, unsigned *start, bool *inverted) const
@ kRound_Cap
adds circle
Definition SkPaint.h:335
@ kButt_Cap
no stroke extension
Definition SkPaint.h:334
@ kDefault_Join
equivalent to kMiter_Join
Definition SkPaint.h:363
@ kRound_Join
adds circle
Definition SkPaint.h:360
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition SkPaint.h:361
static bool IsNestedFillRects(const SkPath &, SkRect rect[2], SkPathDirection dirs[2]=nullptr)
Definition SkPath.cpp:3712
static const uint8_t * VerbData(const SkPath &path)
Definition SkPathPriv.h:198
static void AddGenIDChangeListener(const SkPath &path, sk_sp< SkIDChangeListener > listener)
Definition SkPathPriv.h:105
static const SkScalar * ConicWeightData(const SkPath &path)
Definition SkPathPriv.h:213
static const SkPoint * PointData(const SkPath &path)
Definition SkPathPriv.h:203
static int ConicWeightCnt(const SkPath &path)
Definition SkPathPriv.h:208
bool isInverseFillType() const
Definition SkPath.h:244
uint32_t getGenerationID() const
Definition SkPath.cpp:356
SkPathFillType getFillType() const
Definition SkPath.h:230
void setFillType(SkPathFillType ft)
Definition SkPath.h:235
bool isVolatile() const
Definition SkPath.h:350
bool isConvex() const
Definition SkPath.cpp:416
bool isOval() const
Definition SkRRect.h:85
static SkRRect MakeOval(const SkRect &oval)
Definition SkRRect.h:162
static SkRRect MakeRect(const SkRect &r)
Definition SkRRect.h:149
size_t writeToMemory(void *buffer) const
Definition SkRRect.cpp:599
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition SkRRect.h:180
static constexpr size_t kSizeInMemory
Definition SkRRect.h:422
@ kHairline_InitStyle
Definition SkStrokeRec.h:25
Style getStyle() const
bool needToApply() const
Definition SkStrokeRec.h:84
@ kStrokeAndFill_Style
Definition SkStrokeRec.h:36
void setStrokeStyle(SkScalar width, bool strokeAndFill=false)
bool isHairlineStyle() const
Definition SkStrokeRec.h:47
SkScalar getWidth() const
Definition SkStrokeRec.h:42
void setStrokeParams(SkPaint::Cap cap, SkPaint::Join join, SkScalar miterLimit)
Definition SkStrokeRec.h:65
SkPaint::Join getJoin() const
Definition SkStrokeRec.h:45
SkPaint::Cap getCap() const
Definition SkStrokeRec.h:44
SkScalar getResScale() const
Definition SkStrokeRec.h:71
SkScalar getMiter() const
Definition SkStrokeRec.h:43
T * init(Args &&... args)
Definition SkTLazy.h:45
T * get()
Definition SkTLazy.h:83
void reset()
Definition SkTLazy.h:69
bool isValid() const
Definition SkTLazy.h:77
T * set(const T &src)
Definition SkTLazy.h:56
float SkScalar
Definition extension.cpp:12
GAsyncResult * result
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
SkScalar w
const Scalar scale
SkPoint fP2
Definition GrShape.h:28
SkPoint fP1
Definition GrShape.h:27
Definition SkArc.h:15
bool fUseCenter
Definition SkArc.h:26
float fX
x-axis value
float fY
y-axis value
SkRect makeSorted() const
Definition SkRect.h:1330
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
void outset(float dx, float dy)
Definition SkRect.h:1077
const float * asScalars() const
Definition SkRect.h:1340
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15