Reland "Consolidate quad optimizations into single internal function."

This reverts commit 7694b90eb000cd91813171cb9ad5a2b45d51dcf9.

Reason for revert: suppression: https://chromium-review.googlesource.com/c/chromium/src/+/1666472
Original change's description:
> Revert "Consolidate quad optimizations into single internal function."
>
> This reverts commit 646616a78fe0a003c94a27e79c7e55dcc9ae2d66.
>
> Reason for revert: Suspected as cause of layout test changes.
>
> Original change's description:
> > Consolidate quad optimizations into single internal function.
> >
> > Routes all non-textured quad draws through single internal function
> >
> > Change-Id: Ief66864a0ad2d598982c5bf500c8a84ecbf84387
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215455
> > Commit-Queue: Michael Ludwig <michaelludwig@google.com>
> > Reviewed-by: Robert Phillips <robertphillips@google.com>
>
> TBR=robertphillips@google.com,michaelludwig@google.com
>
> Change-Id: I0dc6a0d948c0f5e9221ff6c9fbbbbbb9bc3d9bc0
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221737
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

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

Change-Id: I4e5d39d603d32b18c48db291fb1650fe33e9ba11
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222096
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/tests/GrQuadCropTest.cpp b/tests/GrQuadCropTest.cpp
new file mode 100644
index 0000000..36fafe4
--- /dev/null
+++ b/tests/GrQuadCropTest.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkScalar.h"
+#include "src/gpu/geometry/GrQuad.h"
+#include "src/gpu/geometry/GrQuadUtils.h"
+#include "tests/Test.h"
+
+#define ASSERT(cond) REPORTER_ASSERT(r, cond)
+#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
+#define TEST(name) DEF_TEST(GrQuadCrop##name, r)
+#define ASSERT_NEARLY_EQUAL(expected, actual) \
+    ASSERTF(SkScalarNearlyEqual(expected, actual), "expected: %f, actual: %f", \
+            expected, actual)
+
+// Make the base rect contain the origin and have unique edge values so that each transform
+// produces a different axis-aligned rectangle.
+static const SkRect kDrawRect = SkRect::MakeLTRB(-5.f, -6.f, 10.f, 11.f);
+
+static void run_crop_axis_aligned_test(skiatest::Reporter* r, const SkRect& clipRect, GrAA clipAA,
+                                       const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
+    // Should use run_crop_fully_covers_test for non-rect matrices
+    SkASSERT(viewMatrix.rectStaysRect());
+
+    GrQuad drawQuad = GrQuad::MakeFromRect(kDrawRect, viewMatrix);
+    GrQuad localQuad = GrQuad::MakeFromRect(kDrawRect, localMatrix ? *localMatrix : SkMatrix::I());
+    GrQuad* localQuadPtr = localMatrix ? &localQuad : nullptr;
+    GrQuadAAFlags edgeFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
+
+    bool exact = GrQuadUtils::CropToRect(clipRect, clipAA, &edgeFlags, &drawQuad, localQuadPtr);
+    ASSERTF(exact, "Expected exact crop");
+    ASSERTF(drawQuad.quadType() == GrQuad::Type::kAxisAligned,
+            "Expected quad to remain axis-aligned");
+
+    // Since we remained a rectangle, the bounds will exactly match the coordinates
+    SkRect expectedBounds = viewMatrix.mapRect(kDrawRect);
+    SkAssertResult(expectedBounds.intersect(clipRect));
+
+    SkRect actualBounds = drawQuad.bounds();
+    ASSERT_NEARLY_EQUAL(expectedBounds.fLeft, actualBounds.fLeft);
+    ASSERT_NEARLY_EQUAL(expectedBounds.fTop, actualBounds.fTop);
+    ASSERT_NEARLY_EQUAL(expectedBounds.fRight, actualBounds.fRight);
+    ASSERT_NEARLY_EQUAL(expectedBounds.fBottom, actualBounds.fBottom);
+
+    // Confirm that local coordinates match up with clipped edges and the transform
+    SkMatrix invViewMatrix;
+    SkAssertResult(viewMatrix.invert(&invViewMatrix));
+
+    if (localMatrix) {
+        SkMatrix toLocal = SkMatrix::Concat(*localMatrix, invViewMatrix);
+
+        for (int p = 0; p < 4; ++p) {
+            SkPoint expectedPoint = drawQuad.point(p);
+            toLocal.mapPoints(&expectedPoint, 1);
+            SkPoint actualPoint = localQuad.point(p);
+
+            ASSERT_NEARLY_EQUAL(expectedPoint.fX, actualPoint.fX);
+            ASSERT_NEARLY_EQUAL(expectedPoint.fY, actualPoint.fY);
+        }
+    }
+
+    // Confirm that the edge flags match, by mapping clip rect to drawRect space and
+    // comparing to the original draw rect edges
+    SkRect drawClip = invViewMatrix.mapRect(clipRect);
+    if (drawClip.fLeft > kDrawRect.fLeft) {
+        if (clipAA == GrAA::kYes) {
+            ASSERTF(edgeFlags & GrQuadAAFlags::kLeft, "Expected left edge AA set");
+        } else {
+            ASSERTF(!(edgeFlags & GrQuadAAFlags::kLeft), "Expected left edge AA unset");
+        }
+    }
+    if (drawClip.fRight < kDrawRect.fRight) {
+        if (clipAA == GrAA::kYes) {
+            ASSERTF(edgeFlags & GrQuadAAFlags::kRight, "Expected right edge AA set");
+        } else {
+            ASSERTF(!(edgeFlags & GrQuadAAFlags::kRight),  "Expected right edge AA unset");
+        }
+    }
+    if (drawClip.fTop > kDrawRect.fTop) {
+        if (clipAA == GrAA::kYes) {
+            ASSERTF(edgeFlags & GrQuadAAFlags::kTop, "Expected top edge AA set");
+        } else {
+            ASSERTF(!(edgeFlags & GrQuadAAFlags::kTop), "Expected top edge AA unset");
+        }
+    }
+    if (drawClip.fBottom < kDrawRect.fBottom) {
+        if (clipAA == GrAA::kYes) {
+            ASSERTF(edgeFlags & GrQuadAAFlags::kBottom, "Expected bottom edge AA set");
+        } else {
+            ASSERTF(!(edgeFlags & GrQuadAAFlags::kBottom), "Expected bottom edge AA unset");
+        }
+    }
+}
+
+static void run_crop_fully_covered_test(skiatest::Reporter* r, GrAA clipAA,
+                                        const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
+    // Should use run_crop_axis_aligned for rect transforms since that verifies more behavior
+    SkASSERT(!viewMatrix.rectStaysRect());
+
+    // Test what happens when the geometry fully covers the crop rect. Given a fixed crop,
+    // use the provided view matrix to derive the "input" geometry that we know covers the crop.
+    SkMatrix invViewMatrix;
+    SkAssertResult(viewMatrix.invert(&invViewMatrix));
+
+    SkRect containsCrop = kDrawRect; // Use kDrawRect as the crop rect for this test
+    containsCrop.outset(10.f, 10.f);
+    SkRect drawRect = invViewMatrix.mapRect(containsCrop);
+
+    GrQuad drawQuad = GrQuad::MakeFromRect(drawRect, viewMatrix);
+    GrQuadAAFlags edgeFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
+
+    if (localMatrix) {
+        GrQuad localQuad = GrQuad::MakeFromRect(drawRect, *localMatrix);
+
+        GrQuad originalDrawQuad = drawQuad;
+        GrQuad originalLocalQuad = localQuad;
+        GrQuadAAFlags originalEdgeFlags = edgeFlags;
+
+        bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &edgeFlags, &drawQuad, &localQuad);
+        // Currently non-rect matrices don't know how to update local coordinates, so the crop
+        // doesn't know how to restrict itself and should leave the inputs unmodified
+        ASSERTF(!exact, "Expected crop to be not exact");
+        ASSERTF(edgeFlags == originalEdgeFlags, "Expected edge flags not to be modified");
+
+        for (int i = 0; i < 4; ++i) {
+            ASSERT_NEARLY_EQUAL(originalDrawQuad.x(i), drawQuad.x(i));
+            ASSERT_NEARLY_EQUAL(originalDrawQuad.y(i), drawQuad.y(i));
+            ASSERT_NEARLY_EQUAL(originalDrawQuad.w(i), drawQuad.w(i));
+
+            ASSERT_NEARLY_EQUAL(originalLocalQuad.x(i), localQuad.x(i));
+            ASSERT_NEARLY_EQUAL(originalLocalQuad.y(i), localQuad.y(i));
+            ASSERT_NEARLY_EQUAL(originalLocalQuad.w(i), localQuad.w(i));
+        }
+    } else {
+        // Since no local coordinates were provided, and the input draw geometry is known to
+        // fully cover the crop rect, the quad should be updated to match cropRect exactly
+        bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &edgeFlags, &drawQuad, nullptr);
+        ASSERTF(exact, "Expected crop to be exact");
+
+        GrQuadAAFlags expectedFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kAll
+                                                           : GrQuadAAFlags::kNone;
+        ASSERTF(expectedFlags == edgeFlags, "Expected edge flags do not match clip AA setting");
+        ASSERTF(drawQuad.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type");
+
+        ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, drawQuad.x(0));
+        ASSERT_NEARLY_EQUAL(kDrawRect.fTop, drawQuad.y(0));
+        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(0));
+
+        ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, drawQuad.x(1));
+        ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, drawQuad.y(1));
+        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(1));
+
+        ASSERT_NEARLY_EQUAL(kDrawRect.fRight, drawQuad.x(2));
+        ASSERT_NEARLY_EQUAL(kDrawRect.fTop, drawQuad.y(2));
+        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(2));
+
+        ASSERT_NEARLY_EQUAL(kDrawRect.fRight, drawQuad.x(3));
+        ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, drawQuad.y(3));
+        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(3));
+    }
+}
+
+static void test_axis_aligned_all_clips(skiatest::Reporter* r, const SkMatrix& viewMatrix,
+                                        const SkMatrix* localMatrix) {
+    static const float kInsideEdge = SkScalarAbs(kDrawRect.fLeft) - 1.f;
+    static const float kOutsideEdge = SkScalarAbs(kDrawRect.fBottom) + 1.f;
+    static const float kIntersectEdge = SkScalarAbs(kDrawRect.fTop) + 1.f;
+
+    static const SkRect kInsideClipRect = SkRect::MakeLTRB(-kInsideEdge, -kInsideEdge,
+                                                           kInsideEdge, kInsideEdge);
+    static const SkRect kContainsClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kOutsideEdge,
+                                                             kOutsideEdge, kOutsideEdge);
+    static const SkRect kXYAxesClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kIntersectEdge,
+                                                           kIntersectEdge, kIntersectEdge);
+    static const SkRect kXAxisClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kOutsideEdge,
+                                                          kIntersectEdge, kOutsideEdge);
+    static const SkRect kYAxisClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kIntersectEdge,
+                                                          kOutsideEdge, kIntersectEdge);
+
+    run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kNo, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kNo, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kNo, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
+
+    run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kYes, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kYes, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kYes, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
+    run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
+}
+
+static void test_axis_aligned(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
+    test_axis_aligned_all_clips(r, viewMatrix, nullptr);
+
+    SkMatrix normalized = SkMatrix::MakeRectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f),
+                                                   SkMatrix::kFill_ScaleToFit);
+    test_axis_aligned_all_clips(r, viewMatrix, &normalized);
+
+    SkMatrix rotated;
+    rotated.setRotate(45.f);
+    test_axis_aligned_all_clips(r, viewMatrix, &rotated);
+
+    SkMatrix perspective;
+    perspective.setPerspY(0.001f);
+    perspective.setSkewX(8.f / 25.f);
+    test_axis_aligned_all_clips(r, viewMatrix, &perspective);
+}
+
+static void test_crop_fully_covered(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
+    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, nullptr);
+    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, nullptr);
+
+    SkMatrix normalized = SkMatrix::MakeRectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f),
+                                                   SkMatrix::kFill_ScaleToFit);
+    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &normalized);
+    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &normalized);
+
+    SkMatrix rotated;
+    rotated.setRotate(45.f);
+    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &rotated);
+    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &rotated);
+
+    SkMatrix perspective;
+    perspective.setPerspY(0.001f);
+    perspective.setSkewX(8.f / 25.f);
+    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &perspective);
+    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &perspective);
+}
+
+TEST(AxisAligned) {
+    test_axis_aligned(r, SkMatrix::I());
+    test_axis_aligned(r, SkMatrix::MakeScale(-1.f, 1.f));
+    test_axis_aligned(r, SkMatrix::MakeScale(1.f, -1.f));
+
+    SkMatrix rotation;
+    rotation.setRotate(90.f);
+    test_axis_aligned(r, rotation);
+    rotation.setRotate(180.f);
+    test_axis_aligned(r, rotation);
+    rotation.setRotate(270.f);
+    test_axis_aligned(r, rotation);
+}
+
+TEST(FullyCovered) {
+    SkMatrix rotation;
+    rotation.setRotate(34.f);
+    test_crop_fully_covered(r, rotation);
+
+    SkMatrix skew;
+    skew.setSkewX(0.3f);
+    skew.setSkewY(0.04f);
+    test_crop_fully_covered(r, skew);
+
+    SkMatrix perspective;
+    perspective.setPerspX(0.001f);
+    perspective.setSkewY(8.f / 25.f);
+    test_crop_fully_covered(r, perspective);
+}