Revert "Revert "Extend SkCanvas matrix stack to be 4x4, but with (basically) the same public API.""

The reason for the assert was breaking an assert, that if the CTM was scale/translate, that after
a preTranslate, it should still be that.

This is true... unless the new translate values are non-finite. In that case, we might turn a zero
into a NaN, (0 * non_finite --> nan), so we either have to require finite args (which we don't
at the moment) or we can't make this assert. This re-land removes that assert.

This reverts commit 268ed57d71e7aa7230d0c1a5722cf84a65f43e61.

Change-Id: I3c48a0aa17649351a246c1fbab5449f2d59aaf84
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/263023
Commit-Queue: Mike Reed <reed@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: Mike Reed <reed@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 2cd4c8f..3ed8f13 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -22,6 +22,7 @@
 #include "include/utils/SkNoDrawCanvas.h"
 #include "src/core/SkArenaAlloc.h"
 #include "src/core/SkBitmapDevice.h"
+#include "src/core/SkCanvasMatrix.h"
 #include "src/core/SkCanvasPriv.h"
 #include "src/core/SkClipOpPriv.h"
 #include "src/core/SkClipStack.h"
@@ -235,7 +236,7 @@
     DeviceCM* fTopLayer;
     std::unique_ptr<BackImage> fBackImage;
     SkConservativeClip fRasterClip;
-    SkMatrix fMatrix;
+    SkCanvasMatrix fMatrix;
     int fDeferredSaveCount;
 
     MCRec() {
@@ -1428,7 +1429,9 @@
         fMCRec->fMatrix.preTranslate(dx,dy);
 
         // Translate shouldn't affect the is-scale-translateness of the matrix.
-        SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
+        // However, if either is non-finite, we might still complicate the matrix type,
+        // so we still have to compute this.
+        fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
 
         FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
 
@@ -1474,6 +1477,25 @@
     this->didConcat(matrix);
 }
 
+#ifndef SK_SUPPORT_LEGACY_CANVAS_MATRIX_33
+// inefficient, just wanted something so we can test with for now
+void SkCanvas::concat(const SkMatrix44& matrix) {
+    this->checkForDeferredSave();
+
+    SkScalar m[16];
+    matrix.asColMajorf(m);
+    SkM44 m44;
+    m44.setColMajor(m);
+    fMCRec->fMatrix.preConcat(m44);
+
+    fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
+
+    FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix));
+
+    this->didConcat44(m);
+}
+#endif
+
 void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
     fMCRec->fMatrix = matrix;
     fIsScaleTranslate = matrix.isScaleTranslate();
@@ -1583,8 +1605,7 @@
     FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA));
 
     const SkPath* rasterClipPath = &path;
-    const SkMatrix* matrix = &fMCRec->fMatrix;
-    fMCRec->fRasterClip.opPath(*rasterClipPath, *matrix, this->getTopLayerBounds(),
+    fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(),
                                (SkRegion::Op)op, isAA);
     fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
 }
@@ -1761,7 +1782,7 @@
     return fMCRec->fRasterClip.getBounds();
 }
 
-const SkMatrix& SkCanvas::getTotalMatrix() const {
+SkMatrix SkCanvas::getTotalMatrix() const {
     return fMCRec->fMatrix;
 }
 
diff --git a/src/core/SkCanvasMatrix.h b/src/core/SkCanvasMatrix.h
new file mode 100644
index 0000000..f532be3
--- /dev/null
+++ b/src/core/SkCanvasMatrix.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCanvasMatrix_DEFINED
+#define SkCanvasMatrix_DEFINED
+
+#include "include/core/SkMatrix.h"
+#include "include/private/SkM44.h"
+
+class SkCanvasMatrix {
+#ifdef SK_SUPPORT_LEGACY_CANVAS_MATRIX_33
+    SkMatrix fM;
+#else
+    SkM44 fM;
+#endif
+public:
+    SkCanvasMatrix& operator=(const SkMatrix& other) { fM = other; return *this; }
+
+    void reset() { fM.setIdentity(); }
+    void preTranslate(SkScalar x, SkScalar y) { fM.preTranslate(x, y); }
+    void preConcat(const SkMatrix& m) { fM.preConcat(m); }
+
+#ifdef SK_SUPPORT_LEGACY_CANVAS_MATRIX_33
+    operator SkMatrix() const { return fM; }
+    bool isScaleTranslate() const { return fM.isScaleTranslate(); }
+    bool rectStaysRect() const { return fM.rectStaysRect(); }
+
+    float getScaleX() const { return fM.getScaleX(); }
+    float getScaleY() const { return fM.getScaleY(); }
+    float getTranslateX() const { return fM.getTranslateX(); }
+    float getTranslateY() const { return fM.getTranslateY(); }
+
+    bool invert(SkMatrix* inv) const { return fM.invert(inv); }
+
+    bool mapRect(SkRect* dst, const SkRect& src) { return fM.mapRect(dst, src); }
+#else
+    operator SkMatrix() const { return fM.asM33(); }
+    // the legacy check was just for the 3x3 portion, so we only check those
+    bool isScaleTranslate() const {
+        return fM.atColMajor(1) == 0 && fM.atColMajor(3) == 0 &&
+               fM.atColMajor(4) == 0 && fM.atColMajor(7) == 0 && fM.atColMajor(15) == 1;
+    }
+    bool rectStaysRect() const { return fM.asM33().rectStaysRect(); }
+
+    float getScaleX() const { return fM.atColMajor(0); }
+    float getScaleY() const { return fM.atColMajor(5); }
+    float getTranslateX() const { return fM.atColMajor(12); }
+    float getTranslateY() const { return fM.atColMajor(13); }
+
+    bool invert(SkMatrix* inv) const { return fM.asM33().invert(inv); }
+
+    bool mapRect(SkRect* dst, const SkRect& src) { return fM.asM33().mapRect(dst, src); }
+
+    void preConcat(const SkM44& m) { fM.setConcat(fM, m); }
+#endif
+};
+
+#endif
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 877b358..ab94f87 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -15,6 +15,7 @@
 #include "include/core/SkShader.h"
 #include "include/core/SkVertices.h"
 #include "include/private/SkTo.h"
+#include "src/core/SkCanvasMatrix.h"
 #include "src/core/SkDraw.h"
 #include "src/core/SkGlyphRun.h"
 #include "src/core/SkImageFilterCache.h"
@@ -46,7 +47,7 @@
     fLocalToDevice.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
 }
 
-void SkBaseDevice::setGlobalCTM(const SkMatrix& ctm) {
+void SkBaseDevice::setGlobalCTM(const SkCanvasMatrix& ctm) {
     fLocalToDevice = ctm;
     fLocalToDevice.normalizePerspective();
     if (fOrigin.fX | fOrigin.fY) {
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index 963d0f0..6bc625d 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -17,6 +17,7 @@
 
 class SkBitmap;
 struct SkDrawShadowRec;
+class SkCanvasMatrix;
 class SkGlyphRun;
 class SkGlyphRunList;
 class SkImageFilterCache;
@@ -99,7 +100,7 @@
     virtual void* getRasterHandle() const { return nullptr; }
 
     void save() { this->onSave(); }
-    void restore(const SkMatrix& ctm) {
+    void restore(const SkCanvasMatrix& ctm) {
         this->onRestore();
         this->setGlobalCTM(ctm);
     }
@@ -130,7 +131,7 @@
     void setLocalToDevice(const SkMatrix& localToDevice) {
         fLocalToDevice = localToDevice;
     }
-    void setGlobalCTM(const SkMatrix& ctm);
+    void setGlobalCTM(const SkCanvasMatrix& ctm);
     virtual void validateDevBounds(const SkIRect&) {}
 
     virtual bool android_utils_clipWithStencil() { return false; }
diff --git a/src/core/SkM44.cpp b/src/core/SkM44.cpp
new file mode 100644
index 0000000..be770f6
--- /dev/null
+++ b/src/core/SkM44.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkMatrix.h"
+#include "include/private/SkM44.h"
+#include "include/private/SkVx.h"
+
+typedef skvx::Vec<4, float> sk4f;
+
+bool SkM44::operator==(const SkM44& other) const {
+    if (this == &other) {
+        return true;
+    }
+
+    sk4f a0 = sk4f::Load(fMat +  0);
+    sk4f a1 = sk4f::Load(fMat +  4);
+    sk4f a2 = sk4f::Load(fMat +  8);
+    sk4f a3 = sk4f::Load(fMat + 12);
+
+    sk4f b0 = sk4f::Load(other.fMat +  0);
+    sk4f b1 = sk4f::Load(other.fMat +  4);
+    sk4f b2 = sk4f::Load(other.fMat +  8);
+    sk4f b3 = sk4f::Load(other.fMat + 12);
+
+    auto eq = (a0 == b0) & (a1 == b1) & (a2 == b2) & (a3 == b3);
+    return (eq[0] & eq[1] & eq[2] & eq[3]) == ~0;
+}
+
+static void transpose(SkScalar dst[], const SkScalar src[]) {
+    dst[0]  = src[0]; dst[1]  = src[4]; dst[2]  = src[8];  dst[3]  = src[12];
+    dst[4]  = src[1]; dst[5]  = src[5]; dst[6]  = src[9];  dst[7]  = src[13];
+    dst[8]  = src[2]; dst[9]  = src[6]; dst[10] = src[10]; dst[11] = src[14];
+    dst[12] = src[3]; dst[13] = src[7]; dst[14] = src[11]; dst[15] = src[15];
+}
+
+void SkM44::getRowMajor(SkScalar v[]) const {
+    transpose(v, fMat);
+}
+
+SkM44& SkM44::setRowMajor(const SkScalar v[]) {
+    transpose(fMat, v);
+    return *this;
+}
+
+SkM44& SkM44::setConcat(const SkM44& a, const SkM44& b) {
+    sk4f c0 = sk4f::Load(a.fMat +  0);
+    sk4f c1 = sk4f::Load(a.fMat +  4);
+    sk4f c2 = sk4f::Load(a.fMat +  8);
+    sk4f c3 = sk4f::Load(a.fMat + 12);
+
+    auto compute = [&](sk4f r) {
+        return skvx::mad(c0, r[0], skvx::mad(c1, r[1], skvx::mad(c2, r[2], c3 * r[3])));
+    };
+
+    sk4f m0 = compute(sk4f::Load(b.fMat +  0));
+    sk4f m1 = compute(sk4f::Load(b.fMat +  4));
+    sk4f m2 = compute(sk4f::Load(b.fMat +  8));
+    sk4f m3 = compute(sk4f::Load(b.fMat + 12));
+
+    m0.store(fMat +  0);
+    m1.store(fMat +  4);
+    m2.store(fMat +  8);
+    m3.store(fMat + 12);
+    return *this;
+}
+
+SkM44& SkM44::preConcat(const SkMatrix& b) {
+    sk4f c0 = sk4f::Load(fMat +  0);
+    sk4f c1 = sk4f::Load(fMat +  4);
+    sk4f c3 = sk4f::Load(fMat + 12);
+
+    auto compute = [&](float r0, float r1, float r3) {
+        return skvx::mad(c0, r0, skvx::mad(c1, r1, c3 * r3));
+    };
+
+    sk4f m0 = compute(b[0], b[3], b[6]);
+    sk4f m1 = compute(b[1], b[4], b[7]);
+    sk4f m3 = compute(b[2], b[5], b[8]);
+
+    m0.store(fMat +  0);
+    m1.store(fMat +  4);
+    m3.store(fMat + 12);
+    return *this;
+}
+
+SkM44& SkM44::preTranslate(SkScalar x, SkScalar y) {
+    sk4f c0 = sk4f::Load(fMat +  0);
+    sk4f c1 = sk4f::Load(fMat +  4);
+    sk4f c3 = sk4f::Load(fMat + 12);
+
+    // only need to update the last column
+    skvx::mad(c0, x, skvx::mad(c1, y, c3)).store(fMat + 12);
+    return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** We always perform the calculation in doubles, to avoid prematurely losing
+    precision along the way. This relies on the compiler automatically
+    promoting our SkMScalar values to double (if needed).
+ */
+double SkM44::determinant() const {
+    double a00 = fMat[0];
+    double a01 = fMat[1];
+    double a02 = fMat[2];
+    double a03 = fMat[3];
+    double a10 = fMat[4];
+    double a11 = fMat[5];
+    double a12 = fMat[6];
+    double a13 = fMat[7];
+    double a20 = fMat[8];
+    double a21 = fMat[9];
+    double a22 = fMat[10];
+    double a23 = fMat[11];
+    double a30 = fMat[12];
+    double a31 = fMat[13];
+    double a32 = fMat[14];
+    double a33 = fMat[15];
+
+    double b00 = a00 * a11 - a01 * a10;
+    double b01 = a00 * a12 - a02 * a10;
+    double b02 = a00 * a13 - a03 * a10;
+    double b03 = a01 * a12 - a02 * a11;
+    double b04 = a01 * a13 - a03 * a11;
+    double b05 = a02 * a13 - a03 * a12;
+    double b06 = a20 * a31 - a21 * a30;
+    double b07 = a20 * a32 - a22 * a30;
+    double b08 = a20 * a33 - a23 * a30;
+    double b09 = a21 * a32 - a22 * a31;
+    double b10 = a21 * a33 - a23 * a31;
+    double b11 = a22 * a33 - a23 * a32;
+
+    // Calculate the determinant
+    return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkM44::invert(SkM44* inverse) const {
+    double a00 = fMat[0];
+    double a01 = fMat[1];
+    double a02 = fMat[2];
+    double a03 = fMat[3];
+    double a10 = fMat[4];
+    double a11 = fMat[5];
+    double a12 = fMat[6];
+    double a13 = fMat[7];
+    double a20 = fMat[8];
+    double a21 = fMat[9];
+    double a22 = fMat[10];
+    double a23 = fMat[11];
+    double a30 = fMat[12];
+    double a31 = fMat[13];
+    double a32 = fMat[14];
+    double a33 = fMat[15];
+
+    double b00 = a00 * a11 - a01 * a10;
+    double b01 = a00 * a12 - a02 * a10;
+    double b02 = a00 * a13 - a03 * a10;
+    double b03 = a01 * a12 - a02 * a11;
+    double b04 = a01 * a13 - a03 * a11;
+    double b05 = a02 * a13 - a03 * a12;
+    double b06 = a20 * a31 - a21 * a30;
+    double b07 = a20 * a32 - a22 * a30;
+    double b08 = a20 * a33 - a23 * a30;
+    double b09 = a21 * a32 - a22 * a31;
+    double b10 = a21 * a33 - a23 * a31;
+    double b11 = a22 * a33 - a23 * a32;
+
+    // Calculate the determinant
+    double det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
+    double invdet = sk_ieee_double_divide(1.0, det);
+    // If det is zero, we want to return false. However, we also want to return false
+    // if 1/det overflows to infinity (i.e. det is denormalized). Both of these are
+    // handled by checking that 1/det is finite.
+    if (!SkScalarIsFinite(SkScalar(invdet))) {
+        return false;
+    }
+
+    b00 *= invdet;
+    b01 *= invdet;
+    b02 *= invdet;
+    b03 *= invdet;
+    b04 *= invdet;
+    b05 *= invdet;
+    b06 *= invdet;
+    b07 *= invdet;
+    b08 *= invdet;
+    b09 *= invdet;
+    b10 *= invdet;
+    b11 *= invdet;
+
+    SkScalar tmp[16] = {
+        SkScalar(a11 * b11 - a12 * b10 + a13 * b09),
+        SkScalar(a02 * b10 - a01 * b11 - a03 * b09),
+        SkScalar(a31 * b05 - a32 * b04 + a33 * b03),
+        SkScalar(a22 * b04 - a21 * b05 - a23 * b03),
+        SkScalar(a12 * b08 - a10 * b11 - a13 * b07),
+        SkScalar(a00 * b11 - a02 * b08 + a03 * b07),
+        SkScalar(a32 * b02 - a30 * b05 - a33 * b01),
+        SkScalar(a20 * b05 - a22 * b02 + a23 * b01),
+        SkScalar(a10 * b10 - a11 * b08 + a13 * b06),
+        SkScalar(a01 * b08 - a00 * b10 - a03 * b06),
+        SkScalar(a30 * b04 - a31 * b02 + a33 * b00),
+        SkScalar(a21 * b02 - a20 * b04 - a23 * b00),
+        SkScalar(a11 * b07 - a10 * b09 - a12 * b06),
+        SkScalar(a00 * b09 - a01 * b07 + a02 * b06),
+        SkScalar(a31 * b01 - a30 * b03 - a32 * b00),
+        SkScalar(a20 * b03 - a21 * b01 + a22 * b00),
+    };
+    if (!SkScalarsAreFinite(tmp, 16)) {
+        return false;
+    }
+    memcpy(inverse->fMat, tmp, sizeof(tmp));
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkM44::dump() const {
+    static const char* format = "|%g %g %g %g|\n"
+                                "|%g %g %g %g|\n"
+                                "|%g %g %g %g|\n"
+                                "|%g %g %g %g|\n";
+    SkDebugf(format,
+             fMat[0], fMat[4], fMat[8],  fMat[12],
+             fMat[1], fMat[5], fMat[9],  fMat[13],
+             fMat[2], fMat[6], fMat[10], fMat[14],
+             fMat[3], fMat[7], fMat[11], fMat[15]);
+}
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 1ccf8c2..535c6b5 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -68,22 +68,6 @@
     return *this;
 }
 
-SkMatrix& SkMatrix::setAll(SkScalar scaleX, SkScalar skewX,  SkScalar transX,
-                           SkScalar skewY,  SkScalar scaleY, SkScalar transY,
-                           SkScalar persp0, SkScalar persp1, SkScalar persp2) {
-    fMat[kMScaleX] = scaleX;
-    fMat[kMSkewX]  = skewX;
-    fMat[kMTransX] = transX;
-    fMat[kMSkewY]  = skewY;
-    fMat[kMScaleY] = scaleY;
-    fMat[kMTransY] = transY;
-    fMat[kMPersp0] = persp0;
-    fMat[kMPersp1] = persp1;
-    fMat[kMPersp2] = persp2;
-    this->setTypeMask(kUnknown_Mask);
-    return *this;
-}
-
 SkMatrix& SkMatrix::setAffine(const SkScalar buffer[]) {
     fMat[kMScaleX] = buffer[kAScaleX];
     fMat[kMSkewX]  = buffer[kASkewX];