Don't let interior AAStrokeRectOp edges cross
Fixes an issue where the interior AA edges of AAStrokeRectOp could
cross over one another, leading to double blends and incorrect
coverage.
Bug: skia:11396
Change-Id: I063defdc9b0691bdd39bc8515fe8a40bac9d29f5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/427977
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrStrokeRectOp.cpp b/src/gpu/ops/GrStrokeRectOp.cpp
index 2ba6ec3..fb12de5 100644
--- a/src/gpu/ops/GrStrokeRectOp.cpp
+++ b/src/gpu/ops/GrStrokeRectOp.cpp
@@ -823,9 +823,31 @@
maybe_coverage(innerCoverage));
// Interior outset rect (away from stroke, toward center of rect).
- vertices.writeQuad(inset_fan(devInside, outset, outset),
- outerColor,
- maybe_coverage(outerCoverage));
+ SkRect interiorAABoundary = devInside.makeInset(outset, outset);
+ float interiorCoverage = outerCoverage;
+ float coverageBackset = 0; // Adds back coverage when the interior AA edges cross.
+ if (interiorAABoundary.fLeft > interiorAABoundary.fRight) {
+ coverageBackset = (interiorAABoundary.fLeft - interiorAABoundary.fRight) / (outset * 2);
+ interiorAABoundary.fLeft = interiorAABoundary.fRight = interiorAABoundary.centerX();
+ }
+ if (interiorAABoundary.fTop > interiorAABoundary.fBottom) {
+ coverageBackset = std::max(
+ (interiorAABoundary.fTop - interiorAABoundary.fBottom) / (outset * 2),
+ coverageBackset);
+ interiorAABoundary.fTop = interiorAABoundary.fBottom = interiorAABoundary.centerY();
+ }
+ if (coverageBackset > 0) {
+ // The interior edges crossed. Lerp back toward innerCoverage, which is what this op
+ // will draw in the degenerate case. This gives a smooth transition into the degenerate
+ // case.
+ interiorCoverage += interiorCoverage * (1 - coverageBackset) +
+ innerCoverage * coverageBackset;
+ }
+ GrVertexColor interiorColor(tweakAlphaForCoverage ? color * interiorCoverage : color,
+ wideColor);
+ vertices.writeQuad(GrVertexWriter::TriFanFromRect(interiorAABoundary),
+ interiorColor,
+ maybe_coverage(interiorCoverage));
} else {
// When the interior rect has become degenerate we smoosh to a single point
SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);