Clip AAStrokeRectOp to viewport boundaries
This avoids interpolation precision issues with very large
coordinates.
Bug: skia:11396
Change-Id: I969a5c7dbd606db5fbbe8ddab775d058f243b9a7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/428281
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 fb12de5..57f28c5 100644
--- a/src/gpu/ops/GrStrokeRectOp.cpp
+++ b/src/gpu/ops/GrStrokeRectOp.cpp
@@ -289,12 +289,16 @@
std::min(devHalfStrokeSize.fX, devHalfStrokeSize.fY) >= .5f;
}
-static void compute_aa_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
- bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
- SkScalar strokeWidth, bool miterStroke, SkVector* devHalfStrokeSize) {
- SkRect devRect;
- viewMatrix.mapRect(&devRect, rect);
-
+static bool compute_aa_rects(const GrCaps& caps,
+ SkRect* devOutside,
+ SkRect* devOutsideAssist,
+ SkRect* devInside,
+ bool* isDegenerate,
+ const SkMatrix& viewMatrix,
+ const SkRect& rect,
+ SkScalar strokeWidth,
+ bool miterStroke,
+ SkVector* devHalfStrokeSize) {
SkVector devStrokeSize;
if (strokeWidth > 0) {
devStrokeSize.set(strokeWidth, strokeWidth);
@@ -312,6 +316,17 @@
devHalfStrokeSize->fX = rx;
devHalfStrokeSize->fY = ry;
+ SkRect devRect;
+ viewMatrix.mapRect(&devRect, rect);
+
+ // Clip our draw rect 1 full stroke width plus bloat outside the viewport. This avoids
+ // interpolation precision issues with very large coordinates.
+ const int m = caps.maxRenderTargetSize();
+ const SkRect visibilityBounds = SkRect::MakeWH(m, m).makeOutset(dx + 1, dy + 1);
+ if (!devRect.intersect(visibilityBounds)) {
+ return false;
+ }
+
*devOutside = devRect;
*devOutsideAssist = devRect;
*devInside = devRect;
@@ -342,6 +357,8 @@
devOutside->inset(0, ry);
devOutsideAssist->outset(0, ry);
}
+
+ return true;
}
static GrGeometryProcessor* create_aa_stroke_rect_gp(SkArenaAlloc* arena,
@@ -423,9 +440,18 @@
return nullptr;
}
RectInfo info;
- compute_aa_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
- &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter,
- &info.fDevHalfStrokeSize);
+ if (!compute_aa_rects(*context->priv().caps(),
+ &info.fDevOutside,
+ &info.fDevOutsideAssist,
+ &info.fDevInside,
+ &info.fDegenerate,
+ viewMatrix,
+ rect,
+ stroke.getWidth(),
+ isMiter,
+ &info.fDevHalfStrokeSize)) {
+ return nullptr;
+ }
if (!stroke_dev_half_size_supported(info.fDevHalfStrokeSize)) {
return nullptr;
}
@@ -888,10 +914,20 @@
SkASSERT(viewMatrix.rectStaysRect());
SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
- SkRect devOutside, devInside;
+ // Clips our draw rects 1 full pixel outside the viewport. This avoids interpolation precision
+ // issues with very large coordinates.
+ const int m = context->priv().caps()->maxRenderTargetSize();
+ const SkRect visibilityBounds = SkRect::MakeWH(m, m).makeOutset(1, 1);
+
+ SkRect devOutside;
viewMatrix.mapRect(&devOutside, rects[0]);
+ if (!devOutside.intersect(visibilityBounds)) {
+ return nullptr;
+ }
+
+ SkRect devInside;
viewMatrix.mapRect(&devInside, rects[1]);
- if (devInside.isEmpty()) {
+ if (devInside.isEmpty() || !devInside.intersect(visibilityBounds)) {
if (devOutside.isEmpty()) {
return nullptr;
}