Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.
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;
139 static_assert(sizeof(SkScalar) == sizeof(uint32_t));
140 SkDEBUGCODE(key += conicWeightCnt);
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().isWedge() ? 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 const GrStyle& style,
319 DoSimplify doSimplify) {
321 result.fShape.setArc(
323 result.fStyle = style;
324 if (doSimplify == DoSimplify::kYes) {
325 result.simplify();
326 }
327 return result;
328}
329
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
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() ||
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());
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
437bool GrStyledShape::asRRect(SkRRect* rrect, bool* inverted) const {
438 if (!fShape.isRRect() && !fShape.isRect()) {
439 return false;
440 }
441
442 if (rrect) {
443 *rrect = fShape.isRect() ? SkRRect::MakeRect(fShape.rect()) : fShape.rrect();
444 }
445
446 if (inverted) {
447 *inverted = fShape.inverted();
448 }
449
450 return true;
451}
452
453bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
454 if (!fShape.isLine()) {
455 return false;
456 }
457
458 if (pts) {
459 pts[0] = fShape.line().fP1;
460 pts[1] = fShape.line().fP2;
461 }
462 if (inverted) {
463 *inverted = fShape.inverted();
464 }
465 return true;
466}
467
469 if (!fShape.isPath()) {
470 return false;
471 }
472
473 // TODO: it would be better two store DRRects natively in the shape rather than converting
474 // them to a path and then reextracting the nested rects
475 if (fShape.path().isInverseFillType()) {
476 return false;
477 }
478
479 SkPathDirection dirs[2];
480 if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
481 return false;
482 }
483
484 if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
485 // The two rects need to be wound opposite to each other
486 return false;
487 }
488
489 // Right now, nested rects where the margin is not the same width
490 // all around do not render correctly
491 const SkScalar* outer = rects[0].asScalars();
492 const SkScalar* inner = rects[1].asScalars();
493
494 bool allEq = true;
495
496 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
497 bool allGoE1 = margin >= SK_Scalar1;
498
499 for (int i = 1; i < 4; ++i) {
500 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
501 if (temp < SK_Scalar1) {
502 allGoE1 = false;
503 }
504 if (!SkScalarNearlyEqual(margin, temp)) {
505 allEq = false;
506 }
507 }
508
509 return allEq || allGoE1;
510}
511
513public:
515 // Dashing ignores inverseness skbug.com/5421.
516 : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
517
519 // Restore invertedness after any modifications were made to the shape type
520 fShape->setInverted(fInverted);
521 SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
522 }
523
524private:
525 GrShape* fShape;
526 bool fInverted;
527};
528
530 AutoRestoreInverseness ari(&fShape, fStyle);
531
532 unsigned simplifyFlags = 0;
533 if (fStyle.isSimpleFill()) {
534 simplifyFlags = GrShape::kAll_Flags;
535 } else if (!fStyle.hasPathEffect()) {
536 // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
537 if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
538 simplifyFlags |= GrShape::kIgnoreWinding_Flag;
539 }
540 simplifyFlags |= GrShape::kMakeCanonical_Flag;
541 } // else if there's a path effect, every destructive simplification is disabledd
542
543 // Remember if the original shape was closed; in the event we simplify to a point or line
544 // because of degenerate geometry, we need to update joins and caps.
545 GrShape::Type oldType = fShape.type();
546 fClosed = fShape.simplify(simplifyFlags);
547 fSimplified = oldType != fShape.type();
548
549 if (fShape.isPath()) {
550 // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
551 if (fInheritedKey.count() || fShape.path().isVolatile()) {
552 fGenID = 0;
553 } else {
554 fGenID = fShape.path().getGenerationID();
555 }
556 if (!fStyle.hasNonDashPathEffect() &&
559 fShape.path().isConvex())) {
560 // Stroke styles don't differentiate between winding and even/odd. There is no
561 // distinction between even/odd and non-zero winding count for convex paths.
562 // Moreover, dashing ignores inverseness (skbug.com/5421)
564 }
565 } else {
566 fInheritedKey.reset(0);
567 // Whenever we simplify to a non-path, break the chain so we no longer refer to the
568 // original path. This prevents attaching genID listeners to temporary paths created when
569 // drawing simple shapes.
570 fInheritedPathForListeners.reset();
571 // Further simplifications to the shape based on the style
572 this->simplifyStroke();
573 }
574}
575
576void GrStyledShape::simplifyStroke() {
577 AutoRestoreInverseness ari(&fShape, fStyle);
578
579 // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
580 // becomes a round rect.
581 if (!fStyle.hasPathEffect() && fShape.isRect() &&
583 if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
584 (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
585 fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
586 // Bevel-stroked rect needs path rendering
587 return;
588 }
589
590 SkScalar r = fStyle.strokeRec().getWidth() / 2;
591 fShape.rect().outset(r, r);
592 if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
593 // There's no dashing to worry about if we got here, so it's okay that this resets
594 // winding parameters
595 fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
596 }
597 fStyle = GrStyle::SimpleFill();
598 fSimplified = true;
599 return;
600 }
601
602 // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
603 // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
604 if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
605 fStyle.strokeRec().isHairlineStyle()) {
606 return;
607 }
608
609 // Tracks style simplifications, even if the geometry can't be further simplified.
610 bool styleSimplified = false;
611 if (fStyle.isDashed()) {
612 // For dashing a point, if the first interval is on, we can drop the dash and just draw
613 // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
614 bool dropDash = false;
615 if (fShape.isPoint()) {
616 dropDash = fStyle.dashIntervalCnt() > 0 &&
617 SkToBool(fStyle.dashIntervals()[0]);
618 } else {
619 dropDash = true;
620 for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
621 if (SkToBool(fStyle.dashIntervals()[i])) {
622 // An off interval has non-zero length so this won't convert to a simple line
623 dropDash = false;
624 break;
625 }
626 }
627 }
628
629 if (!dropDash) {
630 return;
631 }
632 // Fall through to modifying the shape to respect the new stroke geometry
633 fStyle = GrStyle(fStyle.strokeRec(), nullptr);
634 // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
635 // we reset to be unclosed so we don't override the style based on joins later.
636 fClosed = false;
637 styleSimplified = true;
638 }
639
640 // At this point, we're a line or point with no path effects. Any fill portion of the style
641 // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
642 if (fStyle.isSimpleFill()) {
643 fShape.reset();
644 fSimplified = true;
645 return;
646 } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
647 // Stroke only
648 SkStrokeRec rec = fStyle.strokeRec();
649 rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
650 fStyle = GrStyle(rec, nullptr);
651 styleSimplified = true;
652 }
653
654 // A point or line that was formed by a degenerate closed shape needs its style updated to
655 // reflect the fact that it doesn't actually produce caps.
656 if (fClosed) {
657 SkPaint::Cap cap;
658 if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
659 // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
660 // turn. With round joins, this would make a semi-circle at each end, which is visually
661 // identical to a round cap on the reduced line geometry.
663 } else {
664 // If this were a closed line, the 180 degree turn either is a miter join that exceeds
665 // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
666 // of a 180 degreen corner is equivalent to a butt cap.
667 // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
668 // it fits this closed line description (it is not two 90 degree turns that could
669 // produce miter geometry).
670 cap = SkPaint::kButt_Cap;
671 }
672
673 if (cap != fStyle.strokeRec().getCap() ||
675 SkStrokeRec rec = fStyle.strokeRec();
677 fStyle = GrStyle(rec, nullptr);
678 styleSimplified = true;
679 }
680 }
681
682 if (fShape.isPoint()) {
683 // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
684 // doesn't draw anything, a round cap is an oval and a square cap is a square.
685 if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
686 fShape.reset();
687 } else {
688 SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
689 SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
690 r.outset(w, w);
691
692 if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
693 fShape.setRRect(SkRRect::MakeOval(r));
694 } else {
695 fShape.setRect(r);
696 }
697 }
698 } else {
699 // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
700 // allowed rotation angle, this would work for any lines.
701 SkRect rect;
703 if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
704 rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
705 rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
706 rect.fTop = rect.fBottom = fShape.line().fP1.fY;
707 outset.fY = fStyle.strokeRec().getWidth() / 2.f;
708 outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
709 } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
710 rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
711 rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
712 rect.fLeft = rect.fRight = fShape.line().fP1.fX;
713 outset.fX = fStyle.strokeRec().getWidth() / 2.f;
714 outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
715 } else {
716 // Geometrically can't apply the style and turn into a fill, but might still be simpler
717 // than before based solely on changes to fStyle.
718 fSimplified |= styleSimplified;
719 return;
720 }
721 rect.outset(outset.fX, outset.fY);
722 if (rect.isEmpty()) {
723 fShape.reset();
724 } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
725 SkASSERT(outset.fX == outset.fY);
727 } else {
728 fShape.setRect(rect);
729 }
730 }
731 // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
732 fStyle = GrStyle::SimpleFill();
733 fSimplified = true;
734}
SkStrokeRec::Style fStyle
static const int outset
Definition: BlurTest.cpp:58
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
int count
Definition: FontMgrTest.cpp:50
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 SkASSERT(cond)
Definition: SkAssert.h:116
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
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
GrStyledShape fShape
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:49
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
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:244
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
static GrStyledShape MakeArc(const SkArc &arc, const GrStyle &style, DoSimplify=DoSimplify::kYes)
bool asLine(SkPoint pts[2], bool *inverted) const
bool asRRect(SkRRect *rrect, bool *inverted) const
const GrStyle & style() const
SkRect styledBounds() const
SkRect bounds() const
bool asNestedRects(SkRect rects[2]) const
void addGenIDChangeListener(sk_sp< SkIDChangeListener >) const
static constexpr int kMaxKeyFromDataVerbCnt
Definition: GrStyledShape.h:53
static GrStyledShape MakeFilled(const GrStyledShape &original, FillInversion=FillInversion::kPreserve)
GrStyledShape & operator=(const GrStyledShape &that)
@ 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:3780
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:106
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
Definition: SkPath.h:59
bool isInverseFillType() const
Definition: SkPath.h:244
uint32_t getGenerationID() const
Definition: SkPath.cpp:366
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:426
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
Definition: SkStrokeRec.cpp:71
bool needToApply() const
Definition: SkStrokeRec.h:84
@ kStrokeAndFill_Style
Definition: SkStrokeRec.h:36
void setStrokeStyle(SkScalar width, bool strokeAndFill=false)
Definition: SkStrokeRec.cpp:91
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
void reset(int count)
Definition: SkTemplates.h:195
float SkScalar
Definition: extension.cpp:12
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
SkScalar w
const Scalar scale
SkPoint fP2
Definition: GrShape.h:28
SkPoint fP1
Definition: GrShape.h:27
Definition: SkArc.h:15
Type fType
Definition: SkArc.h:62
SkScalar fSweepAngle
Definition: SkArc.h:60
SkScalar fStartAngle
Definition: SkArc.h:58
SkRect fOval
Definition: SkArc.h:55
bool isWedge() const
Definition: SkArc.h:28
static SkArc Make(const SkRect &oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees, Type type)
Definition: SkArc.h:38
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkRect makeSorted() const
Definition: SkRect.h:1330
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
void outset(float dx, float dy)
Definition: SkRect.h:1077
const float * asScalars() const
Definition: SkRect.h:1340