Clean up GrQuad ctors

Refactor Sk4f transformations into reusable internal function.
Switches the SkRect+SkMatrix ctor to a factory method.
Adds simple constructors for Sk4fs and SkRects w/o transforms.

Bug: skia:
Change-Id: I88a4a5f7304b1cf00d68c7772bb0fc19c97abee3
Reviewed-on: https://skia-review.googlesource.com/c/191569
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 0996ec7..8898f18 100644
--- a/src/gpu/GrQuad.cpp
+++ b/src/gpu/GrQuad.cpp
@@ -91,6 +91,53 @@
     return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
 }
 
+static void map_rect_translate_scale(const SkRect& rect, const SkMatrix& m,
+                                     Sk4f* xs, Sk4f* ys) {
+    SkMatrix::TypeMask tm = m.getType();
+    SkASSERT(tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
+
+    Sk4f r = Sk4f::Load(&rect);
+    if (tm > SkMatrix::kIdentity_Mask) {
+        const Sk4f t(m.getTranslateX(), m.getTranslateY(), m.getTranslateX(), m.getTranslateY());
+        if (tm <= SkMatrix::kTranslate_Mask) {
+            r += t;
+        } else {
+            const Sk4f s(m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY());
+            r = r * s + t;
+        }
+    }
+    *xs = SkNx_shuffle<0, 0, 2, 2>(r);
+    *ys = SkNx_shuffle<1, 3, 1, 3>(r);
+}
+
+static void map_quad_general(const Sk4f& qx, const Sk4f& qy, const SkMatrix& m,
+                             Sk4f* xs, Sk4f* ys, Sk4f* ws) {
+    static constexpr auto fma = SkNx_fma<4, float>;
+    *xs = fma(m.getScaleX(), qx, fma(m.getSkewX(), qy, m.getTranslateX()));
+    *ys = fma(m.getSkewY(), qx, fma(m.getScaleY(), qy, m.getTranslateY()));
+    if (m.hasPerspective()) {
+        Sk4f w = fma(m.getPerspX(), qx, fma(m.getPerspY(), qy, m.get(SkMatrix::kMPersp2)));
+        if (ws) {
+            // Output the calculated w coordinates
+            *ws = w;
+        } else {
+            // Apply perspective division immediately
+            Sk4f iw = w.invert();
+            *xs *= iw;
+            *ys *= iw;
+        }
+    } else if (ws) {
+        *ws = 1.f;
+    }
+}
+
+static void map_rect_general(const SkRect& rect, const SkMatrix& matrix,
+                             Sk4f* xs, Sk4f* ys, Sk4f* ws) {
+    Sk4f rx(rect.fLeft, rect.fLeft, rect.fRight, rect.fRight);
+    Sk4f ry(rect.fTop, rect.fBottom, rect.fTop, rect.fBottom);
+    map_quad_general(rx, ry, matrix, xs, ys, ws);
+}
+
 template <typename Q>
 void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
                             const Q& quad, GrQuadType knownType,
@@ -146,41 +193,15 @@
     }
 }
 
-GrQuad::GrQuad(const SkRect& rect, const SkMatrix& m) {
+GrQuad GrQuad::MakeFromRect(const SkRect& rect, const SkMatrix& m) {
+    Sk4f x, y;
     SkMatrix::TypeMask tm = m.getType();
     if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
-        auto r = Sk4f::Load(&rect);
-        const Sk4f t(m.getTranslateX(), m.getTranslateY(), m.getTranslateX(), m.getTranslateY());
-        if (tm <= SkMatrix::kTranslate_Mask) {
-            r += t;
-        } else {
-            const Sk4f s(m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY());
-            r = r * s + t;
-        }
-        SkNx_shuffle<0, 0, 2, 2>(r).store(fX);
-        SkNx_shuffle<1, 3, 1, 3>(r).store(fY);
+        map_rect_translate_scale(rect, m, &x, &y);
     } else {
-        Sk4f rx(rect.fLeft, rect.fLeft, rect.fRight, rect.fRight);
-        Sk4f ry(rect.fTop, rect.fBottom, rect.fTop, rect.fBottom);
-        Sk4f sx(m.getScaleX());
-        Sk4f kx(m.getSkewX());
-        Sk4f tx(m.getTranslateX());
-        Sk4f ky(m.getSkewY());
-        Sk4f sy(m.getScaleY());
-        Sk4f ty(m.getTranslateY());
-        auto x = SkNx_fma(sx, rx, SkNx_fma(kx, ry, tx));
-        auto y = SkNx_fma(ky, rx, SkNx_fma(sy, ry, ty));
-        if (m.hasPerspective()) {
-            Sk4f w0(m.getPerspX());
-            Sk4f w1(m.getPerspY());
-            Sk4f w2(m.get(SkMatrix::kMPersp2));
-            auto iw = SkNx_fma(w0, rx, SkNx_fma(w1, ry, w2)).invert();
-            x *= iw;
-            y *= iw;
-        }
-        x.store(fX);
-        y.store(fY);
+        map_rect_general(rect, m, &x, &y, nullptr);
     }
+    return GrQuad(x, y);
 }
 
 bool GrQuad::aaHasEffectOnRect() const {
@@ -188,43 +209,6 @@
     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)) {
-        auto r = Sk4f::Load(&rect);
-        const Sk4f t(m.getTranslateX(), m.getTranslateY(), m.getTranslateX(), m.getTranslateY());
-        if (tm <= SkMatrix::kTranslate_Mask) {
-            r += t;
-        } else {
-            const Sk4f s(m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY());
-            r = r * s + t;
-        }
-        SkNx_shuffle<0, 0, 2, 2>(r).store(fX);
-        SkNx_shuffle<1, 3, 1, 3>(r).store(fY);
-        fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
-    } else {
-        Sk4f rx(rect.fLeft, rect.fLeft, rect.fRight, rect.fRight);
-        Sk4f ry(rect.fTop, rect.fBottom, rect.fTop, rect.fBottom);
-        Sk4f sx(m.getScaleX());
-        Sk4f kx(m.getSkewX());
-        Sk4f tx(m.getTranslateX());
-        Sk4f ky(m.getSkewY());
-        Sk4f sy(m.getScaleY());
-        Sk4f ty(m.getTranslateY());
-        SkNx_fma(sx, rx, SkNx_fma(kx, ry, tx)).store(fX);
-        SkNx_fma(ky, rx, SkNx_fma(sy, ry, ty)).store(fY);
-        if (m.hasPerspective()) {
-            Sk4f w0(m.getPerspX());
-            Sk4f w1(m.getPerspY());
-            Sk4f w2(m.get(SkMatrix::kMPersp2));
-            auto w = SkNx_fma(w0, rx, SkNx_fma(w1, ry, w2));
-            w.store(fW);
-        } else {
-            fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
-        }
-    }
-}
-
 // Private constructor used by GrQuadList to quickly fill in a quad's values from the channel arrays
 GrPerspQuad::GrPerspQuad(const float* xs, const float* ys, const float* ws) {
     memcpy(fX, xs, 4 * sizeof(float));
@@ -232,6 +216,18 @@
     memcpy(fW, ws, 4 * sizeof(float));
 }
 
+GrPerspQuad GrPerspQuad::MakeFromRect(const SkRect& rect, const SkMatrix& m) {
+    Sk4f x, y, w;
+    SkMatrix::TypeMask tm = m.getType();
+    if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
+        map_rect_translate_scale(rect, m, &x, &y);
+        w = 1.f;
+    } else {
+        map_rect_general(rect, m, &x, &y, &w);
+    }
+    return GrPerspQuad(x, y, w);
+}
+
 bool GrPerspQuad::aaHasEffectOnRect() const {
     SkASSERT(this->quadType() == GrQuadType::kRect);
     // If rect, ws must all be 1s so no need to divide
diff --git a/src/gpu/GrQuad.h b/src/gpu/GrQuad.h
index 33fd326..c754d5a 100644
--- a/src/gpu/GrQuad.h
+++ b/src/gpu/GrQuad.h
@@ -61,13 +61,18 @@
             : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
             , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} {}
 
-    /** Sets the quad to the rect as transformed by the matrix. */
-    GrQuad(const SkRect&, const SkMatrix&);
+    GrQuad(const Sk4f& xs, const Sk4f& ys) {
+        xs.store(fX);
+        ys.store(fY);
+    }
 
     explicit GrQuad(const SkPoint pts[4])
             : fX{pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX}
             , fY{pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY} {}
 
+    /** Sets the quad to the rect as transformed by the matrix. */
+    static GrQuad MakeFromRect(const SkRect&, const SkMatrix&);
+
     GrQuad& operator=(const GrQuad& that) = default;
 
     SkPoint point(int i) const { return {fX[i], fY[i]}; }
@@ -102,7 +107,23 @@
 public:
     GrPerspQuad() = default;
 
-    GrPerspQuad(const SkRect&, const SkMatrix&);
+    explicit GrPerspQuad(const SkRect& rect)
+            : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
+            , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom}
+            , fW{1.f, 1.f, 1.f, 1.f} {}
+
+    GrPerspQuad(const Sk4f& xs, const Sk4f& ys) {
+        xs.store(fX);
+        ys.store(fY);
+        fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
+    }
+    GrPerspQuad(const Sk4f& xs, const Sk4f& ys, const Sk4f& ws) {
+        xs.store(fX);
+        ys.store(fY);
+        ws.store(fW);
+    }
+
+    static GrPerspQuad MakeFromRect(const SkRect&, const SkMatrix&);
 
     GrPerspQuad& operator=(const GrPerspQuad&) = default;
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 427ab9a..7d5918b 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -598,7 +598,7 @@
         }
         // The clip region in the rect's local space, so the test becomes the local rect containing
         // the quad's points.
-        GrQuad quad(rtRect, invM);
+        GrQuad quad = GrQuad::MakeFromRect(rtRect, invM);
         if (!rect_contains_inclusive(rect, quad.point(0)) ||
             !rect_contains_inclusive(rect, quad.point(1)) ||
             !rect_contains_inclusive(rect, quad.point(2)) ||
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index e493607..ca97f29 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -162,7 +162,7 @@
         SkScalar radius = SkScalarHalf(strokeWidth) - 0.5f;
         SkScalar centerX = SkScalarHalf(endInterval);
 
-        vertices.writeQuad(GrQuad(rect, matrix),
+        vertices.writeQuad(GrQuad::MakeFromRect(rect, matrix),
                            GrVertexWriter::TriStripFromRect(dashRect),
                            intervalLength,
                            radius,
@@ -175,7 +175,7 @@
         rectParam.set(halfOffLen                 + 0.5f, -halfStroke + 0.5f,
                       halfOffLen + startInterval - 0.5f,  halfStroke - 0.5f);
 
-        vertices.writeQuad(GrQuad(rect, matrix),
+        vertices.writeQuad(GrQuad::MakeFromRect(rect, matrix),
                            GrVertexWriter::TriStripFromRect(dashRect),
                            intervalLength,
                            rectParam);
@@ -586,7 +586,7 @@
                             draws[i].fLineLength, draws[i].fHalfDevStroke, draws[i].fIntervals[0],
                             draws[i].fIntervals[1], draws[i].fStrokeWidth, capType);
                 } else {
-                    vertices.writeQuad(GrQuad(rects[rectIndex], geom.fSrcRotInv));
+                    vertices.writeQuad(GrQuad::MakeFromRect(rects[rectIndex], geom.fSrcRotInv));
                 }
             }
             rectIndex++;
@@ -599,7 +599,7 @@
                             draws[i].fIntervals[0], draws[i].fHalfDevStroke, draws[i].fIntervals[0],
                             draws[i].fIntervals[1], draws[i].fStrokeWidth, capType);
                 } else {
-                    vertices.writeQuad(GrQuad(rects[rectIndex], geom.fSrcRotInv));
+                    vertices.writeQuad(GrQuad::MakeFromRect(rects[rectIndex], geom.fSrcRotInv));
                 }
             }
             rectIndex++;
@@ -612,7 +612,7 @@
                             draws[i].fIntervals[0], draws[i].fHalfDevStroke, draws[i].fIntervals[0],
                             draws[i].fIntervals[1], draws[i].fStrokeWidth, capType);
                 } else {
-                    vertices.writeQuad(GrQuad(rects[rectIndex], geom.fSrcRotInv));
+                    vertices.writeQuad(GrQuad::MakeFromRect(rects[rectIndex], geom.fSrcRotInv));
                 }
             }
             rectIndex++;
diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp
index bdf1a2e..8dcec21 100644
--- a/src/gpu/ops/GrFillRectOp.cpp
+++ b/src/gpu/ops/GrFillRectOp.cpp
@@ -204,7 +204,8 @@
         void* vertices = vdata;
         if (fHelper.isTrivial()) {
             SkASSERT(fLocalQuads.count() == 0); // No local coords, so send an ignored dummy quad
-            static const GrPerspQuad kIgnoredLocal(SkRect::MakeEmpty(), SkMatrix::I());
+            static const GrPerspQuad kIgnoredLocal(SkRect::MakeEmpty());
+
             for (int i = 0; i < this->quadCount(); ++i) {
                 const ColorAndAA& info = fDeviceQuads.metadata(i);
                 vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i],
@@ -340,9 +341,10 @@
                                       const SkMatrix& viewMatrix,
                                       const SkRect& rect,
                                       const GrUserStencilSettings* stencilSettings) {
+    GrQuadType dstQuadType = GrQuadTypeForTransformedRect(viewMatrix);
     return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings,
-                            GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix),
-                            GrPerspQuad(rect, SkMatrix::I()), GrQuadType::kRect);
+                            GrPerspQuad::MakeFromRect(rect, viewMatrix),  dstQuadType,
+                            GrPerspQuad(rect), GrQuadType::kRect);
 }
 
 std::unique_ptr<GrDrawOp> MakePerEdgeWithLocalMatrix(GrRecordingContext* context,
@@ -353,10 +355,11 @@
                                                      const SkMatrix& localMatrix,
                                                      const SkRect& rect,
                                                      const GrUserStencilSettings* stencilSettings) {
+    GrQuadType dstQuadType = GrQuadTypeForTransformedRect(viewMatrix);
     GrQuadType localQuadType = GrQuadTypeForTransformedRect(localMatrix);
     return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings,
-                            GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix),
-                            GrPerspQuad(rect, localMatrix), localQuadType);
+                            GrPerspQuad::MakeFromRect(rect, viewMatrix), dstQuadType,
+                            GrPerspQuad::MakeFromRect(rect, localMatrix), localQuadType);
 }
 
 std::unique_ptr<GrDrawOp> MakePerEdgeWithLocalRect(GrRecordingContext* context,
@@ -367,9 +370,10 @@
                                                    const SkRect& rect,
                                                    const SkRect& localRect,
                                                    const GrUserStencilSettings* stencilSettings) {
+    GrQuadType dstQuadType = GrQuadTypeForTransformedRect(viewMatrix);
     return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings,
-                            GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix),
-                            GrPerspQuad(localRect, SkMatrix::I()), GrQuadType::kRect);
+                            GrPerspQuad::MakeFromRect(rect, viewMatrix), dstQuadType,
+                            GrPerspQuad(localRect), GrQuadType::kRect);
 }
 
 std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
@@ -385,21 +389,23 @@
 
     paint.setColor4f(quads[0].fColor);
     std::unique_ptr<GrDrawOp> op = FillRectOp::Make(context, std::move(paint), aaType,
-            quads[0].fAAFlags, stencilSettings, GrPerspQuad(quads[0].fRect, viewMatrix),
-            deviceQuadType, GrPerspQuad(quads[0].fRect, quads[0].fLocalMatrix),
+            quads[0].fAAFlags, stencilSettings,
+            GrPerspQuad::MakeFromRect(quads[0].fRect, viewMatrix), deviceQuadType,
+            GrPerspQuad::MakeFromRect(quads[0].fRect, quads[0].fLocalMatrix),
             GrQuadTypeForTransformedRect(quads[0].fLocalMatrix));
     auto* fillRects = op->cast<FillRectOp>();
 
     // Accumulate remaining quads similar to onCombineIfPossible() without creating an op
     for (int i = 1; i < cnt; ++i) {
-        GrPerspQuad deviceQuad(quads[i].fRect, viewMatrix);
+        GrPerspQuad deviceQuad = GrPerspQuad::MakeFromRect(quads[i].fRect, viewMatrix);
 
         GrAAType resolvedAA;
         GrQuadAAFlags resolvedEdgeFlags;
         GrResolveAATypeForQuad(aaType, quads[i].fAAFlags, deviceQuad, deviceQuadType,
                                &resolvedAA, &resolvedEdgeFlags);
 
-        fillRects->addQuad(deviceQuad, GrPerspQuad(quads[i].fRect, quads[i].fLocalMatrix),
+        fillRects->addQuad(deviceQuad,
+                           GrPerspQuad::MakeFromRect(quads[i].fRect, quads[i].fLocalMatrix),
                            GrQuadTypeForTransformedRect(quads[i].fLocalMatrix), quads[i].fColor,
                            resolvedEdgeFlags,resolvedAA);
     }
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index eb3ee7a..7d7e7c8 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -760,8 +760,7 @@
         };
 
         if (fUsesDistanceField && !ctm.hasPerspective()) {
-            GrQuad quad(translatedBounds, ctm);
-            vertices.writeQuad(quad,
+            vertices.writeQuad(GrQuad::MakeFromRect(translatedBounds, ctm),
                                color,
                                texCoords);
         } else {
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 8d44a43..e3c75ce 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -87,7 +87,7 @@
         texRect.fTop = h - texRect.fTop;
         texRect.fBottom = h - texRect.fBottom;
     }
-    return GrPerspQuad(texRect, SkMatrix::I());
+    return GrPerspQuad(texRect);
 }
 
 /**
@@ -201,7 +201,7 @@
             , fFilter(static_cast<unsigned>(filter))
             , fFinalized(0) {
         GrQuadType quadType = GrQuadTypeForTransformedRect(viewMatrix);
-        auto quad = GrPerspQuad(dstRect, viewMatrix);
+        auto quad = GrPerspQuad::MakeFromRect(dstRect, viewMatrix);
 
         // Clean up disparities between the overall aa type and edge configuration and apply
         // optimizations based on the rect and matrix when appropriate
@@ -264,7 +264,7 @@
             if (!fProxies[p].fProxy->canSkipResourceAllocator()) {
                 fCanSkipAllocatorGather = static_cast<unsigned>(false);
             }
-            auto quad = GrPerspQuad(set[p].fDstRect, viewMatrix);
+            auto quad = GrPerspQuad::MakeFromRect(set[p].fDstRect, viewMatrix);
             bounds.joinPossiblyEmptyRect(quad.bounds(quadType));
             GrQuadAAFlags aaFlags;
             // Don't update the overall aaType, might be inappropriate for some of the quads
diff --git a/tests/GrQuadListTest.cpp b/tests/GrQuadListTest.cpp
index fd88dde..fbbfab7 100644
--- a/tests/GrQuadListTest.cpp
+++ b/tests/GrQuadListTest.cpp
@@ -30,7 +30,7 @@
 }
 
 static GrPerspQuad make_2d_persp_quad() {
-    return GrPerspQuad(SkRect::MakeLTRB(5.f, 6.f, 7.f, 8.f), SkMatrix::I());
+    return GrPerspQuad(SkRect::MakeLTRB(5.f, 6.f, 7.f, 8.f));
 }
 static bool is_2d_persp_quad(const GrPerspQuad& quad) {
     return quad.x(0) == 5.f && quad.x(1) == 5.f && quad.x(2) == 7.f && quad.x(3) == 7.f &&
@@ -43,7 +43,7 @@
     SkMatrix p = SkMatrix::I();
     p[SkMatrix::kMPersp2] = 13.f;
     SkASSERT(p.hasPerspective()); // Sanity check
-    return GrPerspQuad(SkRect::MakeLTRB(9.f, 10.f, 11.f, 12.f), p);
+    return GrPerspQuad::MakeFromRect(SkRect::MakeLTRB(9.f, 10.f, 11.f, 12.f), p);
 }
 static bool is_3d_persp_quad(const GrPerspQuad& quad) {
     return quad.x(0) == 9.f && quad.x(1) == 9.f && quad.x(2) == 11.f && quad.x(3) == 11.f &&