Reland "More optimizations for CropToRect, for axis-aligned quads that have not been inverted"

This reverts commit 0437f0f5d89d494199287825335b06b45c4428d9.

Reason for revert: layout tests have been suppressed

Original change's description:
> Revert "More optimizations for CropToRect, for axis-aligned quads that have not been inverted"
> 
> This reverts commit 88a64b4696d5a01e611e69d594088c0ca5667037.
> 
> Reason for revert: Chrome layout tests
> 
> Original change's description:
> > More optimizations for CropToRect, for axis-aligned quads that have not been inverted
> > 
> > Prior to CropToRect(), GrRTC::crop_filled_rect only operated on SkRects. The
> > quad cropping code generalized the optimization to any axis-aligned quad, but
> > a consequence of this is the code had to be robust to flips and 90 degree
> > rotations. While it can handle more cases, it has lead to a 1-10% performance
> > regression on cropped-rectangle-heavy perf tests in chromium.
> > 
> > This change brings back the simplest cropping solution when the axis-aligned
> > quads have not been flipped/rotated, but the general version still exists for
> > the other class of quads as well.
> > 
> > Bug: chromium:980608
> > Change-Id: I83d71075cacc3d849fd9aac6436ea3244a0ae4b9
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/225724
> > Commit-Queue: Brian Salomon <bsalomon@google.com>
> > Auto-Submit: Michael Ludwig <michaelludwig@google.com>
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
> 
> TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com
> 
> Change-Id: I428cbefa5985c6160df0460d38b0698b43d289de
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: chromium:980608
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/225733
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> Commit-Queue: Michael Ludwig <michaelludwig@google.com>

TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com

Change-Id: Ibef8e521f45111b3307731966e19ef66824567e2
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: chromium:980608
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/226177
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index 4c7a983..78edc3c 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -93,16 +93,15 @@
     return false;
 }
 
-// Updates x and y to intersect with clipDevRect, and applies clipAA policy to edgeFlags for each
-// intersected edge. lx, ly, and lw are updated appropriately and may be null to skip calculations.
-static void crop_rect(const SkRect& clipDevRect, GrAA clipAA, GrQuadAAFlags* edgeFlags,
-                      float x[4], float y[4], float lx[4], float ly[4], float lw[4]) {
-    // Filled in as if clipAA were true, will be inverted at the end if needed.
+// Updates x and y to intersect with clipDevRect.  lx, ly, and lw are updated appropriately and may
+// be null to skip calculations. Returns bit mask of edges that were clipped.
+static GrQuadAAFlags crop_rect(const SkRect& clipDevRect, float x[4], float y[4],
+                               float lx[4], float ly[4], float lw[4]) {
     GrQuadAAFlags clipEdgeFlags = GrQuadAAFlags::kNone;
 
-    // However, the quad's left edge may not align with the SkRect notion of left due to 90 degree
-    // rotations or mirrors. So, this processes the logical edges of the quad and clamps it to the 4
-    // sides of clipDevRect.
+    // The quad's left edge may not align with the SkRect notion of left due to 90 degree rotations
+    // or mirrors. So, this processes the logical edges of the quad and clamps it to the 4 sides of
+    // clipDevRect.
 
     // Quad's left is v0 to v1 (op. v2 and v3)
     if (crop_rect_edge(clipDevRect, 0, 1, 2, 3, x, y, lx, ly, lw)) {
@@ -121,13 +120,68 @@
         clipEdgeFlags |= GrQuadAAFlags::kBottom;
     }
 
-    if (clipAA == GrAA::kYes) {
-        // Turn on all edges that were clipped
-        *edgeFlags |= clipEdgeFlags;
-    } else {
-        // Turn off all edges that were clipped
-        *edgeFlags &= ~clipEdgeFlags;
+    return clipEdgeFlags;
+}
+
+// Similar to crop_rect, but assumes that both the device coordinates and optional local coordinates
+// geometrically match the TL, BL, TR, BR vertex ordering, i.e. axis-aligned but not flipped, etc.
+static GrQuadAAFlags crop_simple_rect(const SkRect& clipDevRect, float x[4], float y[4],
+                                      float lx[4], float ly[4]) {
+    GrQuadAAFlags clipEdgeFlags = GrQuadAAFlags::kNone;
+
+    // Update local coordinates proportionately to how much the device rect edge was clipped
+    const SkScalar dx = lx ? (lx[2] - lx[0]) / (x[2] - x[0]) : 0.f;
+    const SkScalar dy = ly ? (ly[1] - ly[0]) / (y[1] - y[0]) : 0.f;
+    if (clipDevRect.fLeft > x[0]) {
+        if (lx) {
+            lx[0] += (clipDevRect.fLeft - x[0]) * dx;
+            lx[1] = lx[0];
+        }
+        x[0] = clipDevRect.fLeft;
+        x[1] = clipDevRect.fLeft;
+        clipEdgeFlags |= GrQuadAAFlags::kLeft;
     }
+    if (clipDevRect.fTop > y[0]) {
+        if (ly) {
+            ly[0] += (clipDevRect.fTop - y[0]) * dy;
+            ly[2] = ly[0];
+        }
+        y[0] = clipDevRect.fTop;
+        y[2] = clipDevRect.fTop;
+        clipEdgeFlags |= GrQuadAAFlags::kTop;
+    }
+    if (clipDevRect.fRight < x[2]) {
+        if (lx) {
+            lx[2] -= (x[2] - clipDevRect.fRight) * dx;
+            lx[3] = lx[2];
+        }
+        x[2] = clipDevRect.fRight;
+        x[3] = clipDevRect.fRight;
+        clipEdgeFlags |= GrQuadAAFlags::kRight;
+    }
+    if (clipDevRect.fBottom < y[1]) {
+        if (ly) {
+            ly[1] -= (y[1] - clipDevRect.fBottom) * dy;
+            ly[3] = ly[1];
+        }
+        y[1] = clipDevRect.fBottom;
+        y[3] = clipDevRect.fBottom;
+        clipEdgeFlags |= GrQuadAAFlags::kBottom;
+    }
+
+    return clipEdgeFlags;
+}
+// Consistent with GrQuad::asRect()'s return value but requires fewer operations since we don't need
+// to calculate the bounds of the quad.
+static bool is_simple_rect(const GrQuad& quad) {
+    if (quad.quadType() != GrQuad::Type::kAxisAligned) {
+        return false;
+    }
+    // v0 at the geometric top-left is unique, so we only need to compare x[0] < x[2] for left
+    // and y[0] < y[1] for top, but add a little padding to protect against numerical precision
+    // on R90 and R270 transforms tricking this check.
+    return ((quad.x(0) + SK_ScalarNearlyZero) < quad.x(2)) &&
+           ((quad.y(0) + SK_ScalarNearlyZero) < quad.y(1));
 }
 
 // Calculates barycentric coordinates for each point in (testX, testY) in the triangle formed by
@@ -198,13 +252,34 @@
     SkASSERT(quad->isFinite());
 
     if (quad->quadType() == GrQuad::Type::kAxisAligned) {
-        // crop_rect keeps the rectangles as rectangles, so there's no need to modify types
+        // crop_rect and crop_rect_simple keep the rectangles as rectangles, so the intersection
+        // of the crop and quad can be calculated exactly. Some care must be taken if the quad
+        // is axis-aligned but does not satisfy asRect() due to flips, etc.
+        GrQuadAAFlags clippedEdges;
         if (local) {
-            crop_rect(cropRect, cropAA, edgeFlags, quad->xs(), quad->ys(),
-                      local->xs(), local->ys(), local->ws());
+            if (is_simple_rect(*quad) && is_simple_rect(*local)) {
+                clippedEdges = crop_simple_rect(cropRect, quad->xs(), quad->ys(),
+                                                local->xs(), local->ys());
+            } else {
+                clippedEdges = crop_rect(cropRect, quad->xs(), quad->ys(),
+                                         local->xs(), local->ys(), local->ws());
+            }
         } else {
-            crop_rect(cropRect, cropAA, edgeFlags, quad->xs(), quad->ys(),
-                      nullptr, nullptr, nullptr);
+            if (is_simple_rect(*quad)) {
+                clippedEdges = crop_simple_rect(cropRect, quad->xs(), quad->ys(), nullptr, nullptr);
+            } else {
+                clippedEdges = crop_rect(cropRect, quad->xs(), quad->ys(),
+                                         nullptr, nullptr, nullptr);
+            }
+        }
+
+        // Apply the clipped edge updates to the original edge flags
+        if (cropAA == GrAA::kYes) {
+            // Turn on all edges that were clipped
+            *edgeFlags |= clippedEdges;
+        } else {
+            // Turn off all edges that were clipped
+            *edgeFlags &= ~clippedEdges;
         }
         return true;
     }