Allow subpixel nested rects to have nearly equal x/y half widths
When transforming to device space, equal local-space half widths in X
and Y might end up being fractionally different, which would prevent
optimizing the nested rect path to use the stroke aa rect op. Visually,
these very small differences are not going to be noticeable and the op
can just arbitrarily pick one of the two widths to use for coverage.
Also fixed an issue where if the aa stroke op returned null from
MakeNested(), drawSimplifiedShape() assumed it meant nothing needed to
be drawn. After https://skia-review.googlesource.com/c/skia/+/427976,
this actually isn't the case and it should attempt path rendering for
the unbalanced subpixel nested rects.
In the linked bug's case, the square rects would not transform to
identical x and y half widths depending on the UI scale of the browser
(differing by a negligible amount). This would then return a null op
and skip falling back to the path renderer so nothing would draw.
Bug: chromium:1234194
Change-Id: Ibd4254cb37e0fc01a5da342e06c4d82cb6223fe8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/447462
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/ops/StrokeRectOp.cpp b/src/gpu/ops/StrokeRectOp.cpp
index c0ce25a..93f969b 100644
--- a/src/gpu/ops/StrokeRectOp.cpp
+++ b/src/gpu/ops/StrokeRectOp.cpp
@@ -284,8 +284,9 @@
// The inner coverage values will be equal if the horizontal and vertical stroke widths are
// equal (in which case innerCoverage is same for all sides of the rects) or if the horizontal
// and vertical stroke widths are both greater than 1 (in which case innerCoverage will always
- // be 1).
- return devHalfStrokeSize.fX == devHalfStrokeSize.fY ||
+ // be 1). In actuality we allow them to be nearly-equal since differing by < 1/1000 will not be
+ // visually detectable when the shape is already less than 1px in thickness.
+ return SkScalarNearlyEqual(devHalfStrokeSize.fX, devHalfStrokeSize.fY) ||
std::min(devHalfStrokeSize.fX, devHalfStrokeSize.fY) >= .5f;
}
@@ -819,11 +820,10 @@
};
// How much do we inset toward the inside of the strokes?
- float inset = .5f;
+ float inset = std::min(0.5f, std::min(devHalfStrokeSize.fX, devHalfStrokeSize.fY));
float innerCoverage = 1;
- if (inset > devHalfStrokeSize.fX) {
- SkASSERT(devHalfStrokeSize.fX == devHalfStrokeSize.fY);
- inset = devHalfStrokeSize.fX;
+ if (inset < 0.5f) {
+ // Stroke is subpixel, so reduce the coverage to simulate the narrower strokes.
innerCoverage = 2 * inset / (inset + .5f);
}