Return coverage from inset() call instead of using a separate function.

This means we don't need to cache the coverage value calculated while
insetting.

Change-Id: Icf8b81f6fac04106ee4cd7d727e363ba0703474e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/251766
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index 62d2d7e..cc44663 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -623,13 +623,12 @@
     }
 }
 
-V4f TessellationHelper::getDegenerateCoverage(const V4f& px, const V4f& py,
-                                              const EdgeEquations& edges) {
+V4f TessellationHelper::EdgeEquations::estimateCoverage(const V4f& x2d, const V4f& y2d) const {
     // Calculate distance of the 4 inset points (px, py) to the 4 edges
-    V4f d0 = mad(edges.fA[0], px, mad(edges.fB[0], py, edges.fC[0]));
-    V4f d1 = mad(edges.fA[1], px, mad(edges.fB[1], py, edges.fC[1]));
-    V4f d2 = mad(edges.fA[2], px, mad(edges.fB[2], py, edges.fC[2]));
-    V4f d3 = mad(edges.fA[3], px, mad(edges.fB[3], py, edges.fC[3]));
+    V4f d0 = mad(fA[0], x2d, mad(fB[0], y2d, fC[0]));
+    V4f d1 = mad(fA[1], x2d, mad(fB[1], y2d, fC[1]));
+    V4f d2 = mad(fA[2], x2d, mad(fB[2], y2d, fC[2]));
+    V4f d3 = mad(fA[3], x2d, mad(fB[3], y2d, fC[3]));
 
     // For each point, pretend that there's a rectangle that touches e0 and e3 on the horizontal
     // axis, so its width is "approximately" d0 + d3, and it touches e1 and e2 on the vertical axis
@@ -643,7 +642,7 @@
     return w * h;
 }
 
-V4f TessellationHelper::computeDegenerateQuad(const V4f& signedEdgeDistances,
+int TessellationHelper::computeDegenerateQuad(const V4f& signedEdgeDistances,
                                               const EdgeEquations& edges,
                                               V4f* x2d, V4f* y2d) {
     // Move the edge by the signed edge adjustment.
@@ -678,33 +677,34 @@
     M4f d1And2 = d1v0 & d2v0;
     M4f d1Or2 = d1v0 | d2v0;
 
-    V4f coverage;
     if (!any(d1Or2)) {
         // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
         // and use full coverage
-        coverage = 1.f;
+        *x2d = px;
+        *y2d = py;
+        return 4;
     } else if (any(d1And2)) {
         // A point failed against two edges, so reduce the shape to a single point, which we take as
         // the center of the original quad to ensure it is contained in the intended geometry. Since
         // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
         SkPoint center = {0.25f * ((*x2d)[0] + (*x2d)[1] + (*x2d)[2] + (*x2d)[3]),
                           0.25f * ((*y2d)[0] + (*y2d)[1] + (*y2d)[2] + (*y2d)[3])};
-        px = center.fX;
-        py = center.fY;
-        coverage = getDegenerateCoverage(px, py, edges);
+        *x2d = center.fX;
+        *y2d = center.fY;
+        return 1;
     } else if (all(d1Or2)) {
         // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
         // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
         if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
             // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
-            px = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
-            py = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
+            *x2d = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
+            *y2d = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
         } else {
             // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
-            px = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
-            py = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
+            *x2d = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
+            *y2d = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
         }
-        coverage = getDegenerateCoverage(px, py, edges);
+        return 2;
     } else {
         // This turns into a triangle. Replace corners as needed with the intersections between
         // (e0,e3) and (e1,e2), which must now be calculated
@@ -725,15 +725,13 @@
             py = if_then_else(d2v0, V4f(ey[1]), py);
         }
 
-        coverage = 1.f;
+        *x2d = px;
+        *y2d = py;
+        return 3;
     }
-
-    *x2d = px;
-    *y2d = py;
-    return coverage;
 }
 
-V4f TessellationHelper::adjustVertices(const OutsetRequest& outsetRequest,
+int TessellationHelper::adjustVertices(const OutsetRequest& outsetRequest,
                                        bool inset,
                                        const EdgeVectors& edgeVectors,
                                        const EdgeEquations* edgeEquations,
@@ -743,7 +741,7 @@
 
     if (fDeviceType == GrQuad::Type::kPerspective || outsetRequest.fDegenerate) {
         Vertices projected = { edgeVectors.fX2D, edgeVectors.fY2D, /*w*/ 1.f, 0.f, 0.f, 0.f, 0};
-        V4f coverage = 1.f;
+        int vertexCount;
         if (outsetRequest.fDegenerate) {
             // Must use the slow path to handle numerical issues and self intersecting geometry
             SkASSERT(edgeEquations);
@@ -751,20 +749,21 @@
             if (inset) {
                 signedEdgeDistances *= -1.f;
             }
-            coverage = computeDegenerateQuad(signedEdgeDistances, *edgeEquations,
-                                             &projected.fX, &projected.fY);
+            vertexCount = computeDegenerateQuad(signedEdgeDistances, *edgeEquations,
+                                                &projected.fX, &projected.fY);
         } else {
             // Move the projected quad with the fast path, even though we will reconstruct the
             // perspective corners afterwards.
             projected.moveAlong(edgeVectors, outsetRequest, inset);
+            vertexCount = 4;
         }
         vertices->moveTo(projected.fX, projected.fY, outsetRequest.fEdgeDistances != 0.f);
-        return coverage;
+        return vertexCount;
     } else {
         // Quad is 2D and the inset/outset request does not cause the geometry to self intersect, so
         // we can directly move the corners along the already calculated edge vectors.
         vertices->moveAlong(edgeVectors, outsetRequest, inset);
-        return 1.f;
+        return 4;
     }
 }
 
@@ -787,13 +786,7 @@
     }
 }
 
-V4f TessellationHelper::pixelCoverage() {
-    // When there are no AA edges, insetting and outsetting is skipped since the original geometry
-    // can just be reported directly (in which case fCoverage may be stale).
-    return fAAFlags == GrQuadAAFlags::kNone ? 1.f : fCoverage;
-}
-
-void TessellationHelper::inset(GrQuadAAFlags aaFlags, GrQuad* deviceInset, GrQuad* localInset) {
+V4f TessellationHelper::inset(GrQuadAAFlags aaFlags, GrQuad* deviceInset, GrQuad* localInset) {
     if (aaFlags != fAAFlags) {
         fAAFlags = aaFlags;
         if (aaFlags != GrQuadAAFlags::kNone) {
@@ -802,8 +795,10 @@
     }
     if (fAAFlags == GrQuadAAFlags::kNone) {
         this->setQuads(fOriginal, deviceInset, localInset);
+        return 1.f;
     } else {
         this->setQuads(fInset, deviceInset, localInset);
+        return fCoverage;
     }
 }
 
@@ -835,11 +830,24 @@
         // adjustVertices requires edge equations too
         EdgeEquations edgeEquations = this->getEdgeEquations(edgeVectors);
         this->adjustVertices(outsetRequest, false, edgeVectors, &edgeEquations, &fOutset);
-        fCoverage = this->adjustVertices(outsetRequest, true, edgeVectors, &edgeEquations, &fInset);
+        int innerVertexCount = this->adjustVertices(outsetRequest, true, edgeVectors,
+                                                    &edgeEquations, &fInset);
+        if (innerVertexCount < 3) {
+            // The interior has less than a full pixel's area so estimate reduced coverage using
+            // the distance of the inset's projected corners to the original edges.
+            fCoverage = edgeEquations.estimateCoverage(fInset.fX / fInset.fW,
+                                                       fInset.fY / fInset.fW);
+        } else {
+            fCoverage = 1.f;
+        }
     } else {
         // skip calculating edge equations
         this->adjustVertices(outsetRequest, false, edgeVectors, nullptr, &fOutset);
-        fCoverage = this->adjustVertices(outsetRequest, true, edgeVectors, nullptr, &fInset);
+        int innerVertexCount = this->adjustVertices(outsetRequest, true, edgeVectors,
+                                                    nullptr, &fInset);
+        (void) innerVertexCount; // silence clang
+        SkASSERT(innerVertexCount == 4);
+        fCoverage = 1.f;
     }
 }