add SkMatrix::decomposeScale

BUG=skia:

Review URL: https://codereview.chromium.org/1006173005
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index a272051..b2ee3c6 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -624,6 +624,19 @@
     bool getMinMaxScales(SkScalar scaleFactors[2]) const;
 
     /**
+     *  Attempt to decompose this matrix into a scale-only component and whatever remains, where
+     *  the scale component is to be applied first.
+     *
+     *  M -> Remaining * Scale
+     *
+     *  On success, return true and assign the scale and remaining components (assuming their
+     *  respective parameters are not null). On failure return false and ignore the parameters.
+     *
+     *  Possible reasons to fail: perspective, one or more scale factors are zero.
+     */
+    bool decomposeScale(SkSize* scale, SkMatrix* remaining = NULL) const;
+
+    /**
      *  Return a reference to a const identity matrix
      */
     static const SkMatrix& I();
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 720a30f..0b50fbc 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -98,12 +98,6 @@
     return (dimension & ~0x3FFF) == 0;
 }
 
-static SkScalar effective_matrix_scale(const SkMatrix& mat) {
-    SkScalar dx = SkVector::Length(mat.getScaleX(), mat.getSkewY());
-    SkScalar dy = SkVector::Length(mat.getSkewX(), mat.getScaleY());
-    return SkScalarSqrt(dx * dy);
-}
-
 // Check to see that the size of the bitmap that would be produced by
 // scaling by the given inverted matrix is less than the maximum allowed.
 static inline bool cache_size_okay(const SkBitmap& bm, const SkMatrix& invMat) {
@@ -119,22 +113,6 @@
 }
 
 /*
- *  Extract the "best" scale factors from a matrix.
- */
-static bool extract_scale(const SkMatrix& matrix, SkVector* scale) {
-    SkASSERT(!matrix.hasPerspective());
-    SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]);
-    SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX],  matrix[SkMatrix::kMScaleY]);
-    if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
-        SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy))
-    {
-        return false;
-    }
-    scale->set(sx, sy);
-    return true;
-}
-
-/*
  *  High quality is implemented by performing up-right scale-only filtering and then
  *  using bilerp for any remaining transformations.
  */
@@ -154,12 +132,12 @@
     SkScalar invScaleX = fInvMatrix.getScaleX();
     SkScalar invScaleY = fInvMatrix.getScaleY();
     if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
-        SkVector scale;
-        if (!extract_scale(fInvMatrix, &scale)) {
-            return; // can't find suitable scale factors
+        SkSize scale;
+        if (!fInvMatrix.decomposeScale(&scale)) {
+            return;
         }
-        invScaleX = scale.x();
-        invScaleY = scale.y();
+        invScaleX = scale.width();
+        invScaleY = scale.height();
     }
     if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
         return; // no need for HQ
@@ -204,7 +182,11 @@
     // to a valid bitmap.
     fFilterLevel = kLow_SkFilterQuality;
 
-    SkScalar invScale = effective_matrix_scale(fInvMatrix);
+    SkSize invScaleSize;
+    if (!fInvMatrix.decomposeScale(&invScaleSize, NULL)) {
+        return;
+    }
+    SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height());
 
     if (invScale > SK_Scalar1) {
         fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 9658177..753c4dc 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1603,6 +1603,28 @@
     return invalid.asSkMatrix();
 }
 
+bool SkMatrix::decomposeScale(SkSize* scale, SkMatrix* remaining) const {
+    if (this->hasPerspective()) {
+        return false;
+    }
+
+    const SkScalar sx = SkVector::Length(this->getScaleX(), this->getSkewY());
+    const SkScalar sy = SkVector::Length(this->getSkewX(), this->getScaleY());
+    if (!SkScalarIsFinite(sx) || !SkScalarIsFinite(sy) ||
+        SkScalarNearlyZero(sx) || SkScalarNearlyZero(sy)) {
+        return false;
+    }
+
+    if (scale) {
+        scale->set(sx, sy);
+    }
+    if (remaining) {
+        *remaining = *this;
+        remaining->postScale(SkScalarInvert(sx), SkScalarInvert(sy));
+    }
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 size_t SkMatrix::writeToMemory(void* buffer) const {
diff --git a/src/core/SkPictureShader.cpp b/src/core/SkPictureShader.cpp
index 776fe5e..3734791 100644
--- a/src/core/SkPictureShader.cpp
+++ b/src/core/SkPictureShader.cpp
@@ -152,6 +152,9 @@
 
     // Use a rotation-invariant scale
     SkPoint scale;
+    //
+    // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
+    //
     if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
         // Decomposition failed, use an approximation.
         scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index 7875d9e..95d33ac 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -771,6 +771,34 @@
 
 }
 
+static bool check_decompScale(const SkMatrix& matrix) {
+    SkSize scale;
+    SkMatrix remaining;
+
+    if (!matrix.decomposeScale(&scale, &remaining)) {
+        return false;
+    }
+    if (scale.width() <= 0 || scale.height() <= 0) {
+        return false;
+    }
+    remaining.preScale(scale.width(), scale.height());
+    return nearly_equal(matrix, remaining);
+}
+
+static void test_decompScale(skiatest::Reporter* reporter) {
+    SkMatrix m;
+
+    m.reset();
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+    m.setScale(2, 3);
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+    m.setRotate(35, 0, 0);
+    REPORTER_ASSERT(reporter, check_decompScale(m));
+
+    m.setScale(1, 0);
+    REPORTER_ASSERT(reporter, !check_decompScale(m));
+}
+
 DEF_TEST(Matrix, reporter) {
     SkMatrix    mat, inverse, iden1, iden2;
 
@@ -889,6 +917,8 @@
     test_matrix_decomposition(reporter);
     test_matrix_homogeneous(reporter);
     test_set9(reporter);
+
+    test_decompScale(reporter);
 }
 
 DEF_TEST(Matrix_Concat, r) {