Add quad type and persp/aa utilities to GrQuad

This refactor makes some of the quad logic in GrTextureOp available for
use with other quad GrOps.

Bug: skia:
Change-Id: I1c173cfdf61b33c8422ddd8b91406a970a1c8e5d
Reviewed-on: https://skia-review.googlesource.com/c/163253
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/GrQuad.cpp b/src/gpu/GrQuad.cpp
index 93bd761..5adb575 100644
--- a/src/gpu/GrQuad.cpp
+++ b/src/gpu/GrQuad.cpp
@@ -7,6 +7,59 @@
 
 #include "GrQuad.h"
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Functions for identifying the quad type from its coordinates, which are kept debug-only since
+// production code should rely on the matrix to derive the quad type more efficiently. These are
+// useful in asserts that the quad type is as expected.
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+// Allow some tolerance from floating point matrix transformations, but SkScalarNearlyEqual doesn't
+// support comparing infinity, and coords_form_rect should return true for infinite edges
+#define NEARLY_EQUAL(f1, f2) (f1 == f2 || SkScalarNearlyEqual(f1, f2, 1e-5f))
+
+// This is not the most performance critical function; code using GrQuad should rely on the faster
+// quad type from matrix path, so this will only be called as part of SkASSERT.
+static bool coords_form_rect(const float xs[4], const float ys[4]) {
+    return (NEARLY_EQUAL(xs[0], xs[1]) && NEARLY_EQUAL(xs[2], xs[3]) &&
+            NEARLY_EQUAL(ys[0], ys[2]) && NEARLY_EQUAL(ys[1], ys[3])) ||
+           (NEARLY_EQUAL(xs[0], xs[2]) && NEARLY_EQUAL(xs[1], xs[3]) &&
+            NEARLY_EQUAL(ys[0], ys[1]) && NEARLY_EQUAL(ys[2], ys[3]));
+}
+
+GrQuadType GrQuad::quadType() const {
+    // Since GrQuad applies any perspective information at construction time, there's only two
+    // types to choose from.
+    return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType : GrQuadType::kStandard_QuadType;
+}
+
+GrQuadType GrPerspQuad::quadType() const {
+    if (this->hasPerspective()) {
+        return GrQuadType::kPerspective_QuadType;
+    } else {
+        // Rect or standard quad, can ignore w since they are all ones
+        return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType
+                                        : GrQuadType::kStandard_QuadType;
+    }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool aa_affects_rect(float ql, float qt, float qr, float qb) {
+    return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
+}
+
+GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) {
+    if (matrix.rectStaysRect()) {
+        return GrQuadType::kRect_QuadType;
+    } else if (matrix.hasPerspective()) {
+        return GrQuadType::kPerspective_QuadType;
+    } else {
+        return GrQuadType::kStandard_QuadType;
+    }
+}
+
 GrQuad::GrQuad(const SkRect& rect, const SkMatrix& m) {
     SkMatrix::TypeMask tm = m.getType();
     if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
@@ -44,6 +97,11 @@
     }
 }
 
+bool GrQuad::aaHasEffectOnRect() const {
+    SkASSERT(this->quadType() == GrQuadType::kRect_QuadType);
+    return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
+}
+
 GrPerspQuad::GrPerspQuad(const SkRect& rect, const SkMatrix& m) {
     SkMatrix::TypeMask tm = m.getType();
     if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
@@ -83,3 +141,9 @@
         }
     }
 }
+
+bool GrPerspQuad::aaHasEffectOnRect() const {
+    SkASSERT(this->quadType() == GrQuadType::kRect_QuadType);
+    // If rect, ws must all be 1s so no need to divide
+    return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
+}
diff --git a/src/gpu/GrQuad.h b/src/gpu/GrQuad.h
index 4a104aa..94fe745 100644
--- a/src/gpu/GrQuad.h
+++ b/src/gpu/GrQuad.h
@@ -13,6 +13,24 @@
 #include "SkPoint.h"
 #include "SkPoint3.h"
 
+// Rectangles transformed by matrices (view or local) can be classified in three ways:
+//  1. Stays a rectangle - the matrix rectStaysRect() is true, or x(0) == x(1) && x(2) == x(3)
+//     and y(0) == y(2) && y(1) == y(3). Or under mirrors, x(0) == x(2) && x(1) == x(3) and
+//     y(0) == y(1) && y(2) == y(3).
+//  2. Is a quadrilateral - the matrix does not have perspective, but may rotate or skew, or
+//     ws() == all ones.
+//  3. Is a perspective quad - the matrix has perspective, subsuming all previous quad types.
+enum class GrQuadType {
+    kRect_QuadType,
+    kStandard_QuadType,
+    kPerspective_QuadType
+};
+
+// If an SkRect is transformed by this matrix, what class of quad is required to represent it. Since
+// quadType() is only provided on Gr[Persp]Quad in debug builds, production code should use this
+// to efficiently determine quad types.
+GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix);
+
 /**
  * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The
  * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right).
@@ -49,6 +67,13 @@
     Sk4f x4f() const { return Sk4f::Load(fX); }
     Sk4f y4f() const { return Sk4f::Load(fY); }
 
+    // True if anti-aliasing affects this quad. Requires quadType() == kRect_QuadType
+    bool aaHasEffectOnRect() const;
+
+#ifdef SK_DEBUG
+    GrQuadType quadType() const;
+#endif
+
 private:
     float fX[4];
     float fY[4];
@@ -80,6 +105,15 @@
     Sk4f w4f() const { return Sk4f::Load(fW); }
     Sk4f iw4f() const { return Sk4f::Load(fIW); }
 
+    bool hasPerspective() const { return (w4f() != Sk4f(1.f)).anyTrue(); }
+
+    // True if anti-aliasing affects this quad. Requires quadType() == kRect_QuadType
+    bool aaHasEffectOnRect() const;
+
+#ifdef SK_DEBUG
+    GrQuadType quadType() const;
+#endif
+
 private:
     float fX[4];
     float fY[4];
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 0c44446..f7a305f 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -351,7 +351,7 @@
                                             GrQuadAAFlags aaFlags, const SkRect& texRect) {
         // Should be kNone for non-AA and kAll for MSAA.
         SkASSERT(aaFlags == GrQuadAAFlags::kNone || aaFlags == GrQuadAAFlags::kAll);
-        SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
+        SkASSERT(!quad.hasPerspective());
         SkPointPriv::SetRectTriStrip(&vertices[0].fTextureCoords, texRect, sizeof(V));
         for (int i = 0; i < 4; ++i) {
             vertices[i].fPosition = {quad.x(i), quad.y(i)};
@@ -376,7 +376,7 @@
 public:
     static void AssignPositionsAndTexCoords(V* vertices, const GrPerspQuad& quad,
                                             GrQuadAAFlags aaFlags, const SkRect& texRect) {
-        SkASSERT((quad.w4f() == Sk4f(1.f)).allTrue());
+        SkASSERT(!quad.hasPerspective());
         if (aaFlags == GrQuadAAFlags::kNone) {
             for (int i = 0; i < 4; ++i) {
                 vertices[i].fPosition = {quad.x(i), quad.y(i)};
@@ -593,17 +593,8 @@
     DomainAssigner<V>::Assign(vertices, domain, filter, srcRect, origin, iw, ih);
 }
 
-static bool aa_has_effect_for_rect_stays_rect(const GrPerspQuad& quad) {
-    SkASSERT((quad.w4f() == Sk4f(1)).allTrue());
-    float ql = quad.x(0);
-    float qt = quad.y(0);
-    float qr = quad.x(3);
-    float qb = quad.y(3);
-    return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
-}
-
 static bool filter_has_effect_for_rect_stays_rect(const GrPerspQuad& quad, const SkRect& srcRect) {
-    SkASSERT((quad.w4f() == Sk4f(1)).allTrue());
+    SkASSERT(quad.quadType() == GrQuadType::kRect_QuadType);
     float ql = quad.x(0);
     float qt = quad.y(0);
     float qr = quad.x(3);
@@ -751,7 +742,7 @@
         SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
                  constraint == SkCanvas::kFast_SrcRectConstraint);
         if (viewMatrix.rectStaysRect()) {
-            if (this->aaType() == GrAAType::kCoverage && !aa_has_effect_for_rect_stays_rect(quad)) {
+            if (this->aaType() == GrAAType::kCoverage && !quad.aaHasEffectOnRect()) {
                 fAAType = static_cast<unsigned>(GrAAType::kNone);
                 aaFlags = GrQuadAAFlags::kNone;
             }
@@ -811,8 +802,7 @@
                     break;
                 case GrAAType::kCoverage:
                     if (rectStaysRect) {
-                        if (aaFlags != GrQuadAAFlags::kNone &&
-                            !aa_has_effect_for_rect_stays_rect(quad)) {
+                        if (aaFlags != GrQuadAAFlags::kNone && !quad.aaHasEffectOnRect()) {
                             aaFlags = GrQuadAAFlags::kNone;
                         }
                     }