Snap coordinates for shader nearest-neighbor decal filtering

Bug: skia:10403
Change-Id: I875b1a4bb7cacbe6721a69aa8ed02118b989729d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/297596
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/effects/GrTextureEffect.cpp b/src/gpu/effects/GrTextureEffect.cpp
index 0f1b0fb..5113cbf 100644
--- a/src/gpu/effects/GrTextureEffect.cpp
+++ b/src/gpu/effects/GrTextureEffect.cpp
@@ -671,17 +671,20 @@
                 }
 
                 // Do hard-edge shader transition to border color for kClampToBorderNearest at the
-                // subset boundaries.
+                // subset boundaries. Snap the input coordinates to nearest neighbor (with an
+                // epsilon) before comparing to the subset rect to avoid GPU interpolation errors
                 if (m[0] == ShaderMode::kClampToBorderNearest) {
                     fb->codeAppendf(
-                            "if (inCoord.x < %s.x || inCoord.x > %s.z) {"
+                            "float snappedX = floor(inCoord.x + 0.001) + 0.5;"
+                            "if (snappedX < %s.x || snappedX > %s.z) {"
                             "    textureColor = %s;"
                             "}",
                             subsetName, subsetName, borderName);
                 }
                 if (m[1] == ShaderMode::kClampToBorderNearest) {
                     fb->codeAppendf(
-                            "if (inCoord.y < %s.y || inCoord.y > %s.w) {"
+                            "float snappedY = floor(inCoord.y + 0.001) + 0.5;"
+                            "if (snappedY < %s.y || snappedY > %s.w) {"
                             "    textureColor = %s;"
                             "}",
                             subsetName, subsetName, borderName);