Handle non-finite quads in attemptQuadOptimization

Bug: chromium:977315
Change-Id: Ia5b734f5c0f0806af0f096de5add880a777c5c25
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222793
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 7780446..02ba6d9 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -537,6 +537,21 @@
     kCropped
 };
 
+static bool make_vertex_finite(float* value) {
+    if (SkScalarIsNaN(*value)) {
+        return false;
+    }
+
+    if (!SkScalarIsFinite(*value)) {
+        // +/- infinity at this point. Don't use exactly SK_ScalarMax so that we have some precision
+        // left when calculating crops.
+        static constexpr float kNearInfinity = SK_ScalarMax / 4.f;
+        *value = *value < 0.f ? -kNearInfinity : kNearInfinity;
+    }
+
+    return true;
+}
+
 GrRenderTargetContext::QuadOptimization GrRenderTargetContext::attemptQuadOptimization(
         const GrClip& clip, const SkPMColor4f* constColor,
         const GrUserStencilSettings* stencilSettings, GrAA* aa, GrQuadAAFlags* edgeFlags,
@@ -567,6 +582,25 @@
     if (constColor) {
         // Don't bother updating local coordinates when the paint will ignore them anyways
         localQuad = nullptr;
+        // If the device quad is not finite, coerce into a finite quad. This is acceptable since it
+        // will be cropped to the finite 'clip' or render target and there is no local space mapping
+        if (!deviceQuad->isFinite()) {
+            for (int i = 0; i < 4; ++i) {
+                if (!make_vertex_finite(deviceQuad->xs() + i) ||
+                    !make_vertex_finite(deviceQuad->ys() + i) ||
+                    !make_vertex_finite(deviceQuad->ws() + i)) {
+                    // Discard if we see a nan
+                    return QuadOptimization::kDiscarded;
+                }
+            }
+            SkASSERT(deviceQuad->isFinite());
+        }
+    } else {
+        // CropToRect requires the quads to be finite. If they are not finite and we have local
+        // coordinates, the mapping from local space to device space is poorly defined so drop it
+        if (!deviceQuad->isFinite()) {
+            return QuadOptimization::kDiscarded;
+        }
     }
 
     // If the quad is entirely off screen, it doesn't matter what the clip does
diff --git a/src/gpu/geometry/GrQuad.h b/src/gpu/geometry/GrQuad.h
index 9a080f3..ea3cdec 100644
--- a/src/gpu/geometry/GrQuad.h
+++ b/src/gpu/geometry/GrQuad.h
@@ -100,6 +100,18 @@
         return {min(x), min(y), max(x), max(y)};
     }
 
+    bool isFinite() const {
+        // If any coordinate is infinity or NaN, then multiplying it with 0 will make accum NaN
+        float accum = 0;
+        for (int i = 0; i < 4; ++i) {
+            accum *= fX[i];
+            accum *= fY[i];
+            accum *= fW[i];
+        }
+        SkASSERT(0 == accum || SkScalarIsNaN(accum));
+        return !SkScalarIsNaN(accum);
+    }
+
     float x(int i) const { return fX[i]; }
     float y(int i) const { return fY[i]; }
     float w(int i) const { return fW[i]; }
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index fce38d2..c4e86d7 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -198,6 +198,8 @@
 
 bool CropToRect(const SkRect& cropRect, GrAA cropAA, GrQuadAAFlags* edgeFlags, GrQuad* quad,
                 GrQuad* local) {
+    SkASSERT(quad->isFinite());
+
     if (quad->quadType() == GrQuad::Type::kAxisAligned) {
         // crop_rect keeps the rectangles as rectangles, so there's no need to modify types
         if (local) {
diff --git a/src/gpu/geometry/GrQuadUtils.h b/src/gpu/geometry/GrQuadUtils.h
index 5f6d283..53b53d2 100644
--- a/src/gpu/geometry/GrQuadUtils.h
+++ b/src/gpu/geometry/GrQuadUtils.h
@@ -25,7 +25,7 @@
      * Crops quad to the provided device-space axis-aligned rectangle. If the intersection of this
      * quad (projected) and cropRect results in a quadrilateral, this returns true. If not, this
      * quad may be updated to be a smaller quad of the same type such that its intersection with
-     * cropRect is visually the same.
+     * cropRect is visually the same. This function assumes that the 'quad' coordinates are finite.
      *
      * The provided edge flags are updated to reflect edges clipped by cropRect (toggling on or off
      * based on cropAA policy). If provided, the local coordinates will be updated to reflect the