Factor text size device mapping in SkScalerContext.

All of our font back-ends use the text size in some way other than
simple concatentation with the current matrix. The code here finds
the full device matrix and then decomposes it to extract the text
size.

FreeType, GDI, and DirectWrite use the text size as the pre-hint scale.
CoreText will not scale color emoji above the requested text size,
and certain features like 'trak' are performed on the text size.

Review URL: https://codereview.chromium.org/748883005
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index d5b7985..94af9ec 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -817,56 +817,12 @@
     }
     fFace = fFaceRec->fFace;
 
-    // A is the total matrix.
-    SkMatrix A;
-    fRec.getSingleMatrix(&A);
+    fRec.computeMatrices(SkScalerContextRec::kFull_PreMatrixScale, &fScale, &fMatrix22Scalar);
+    fMatrix22Scalar.setSkewX(-fMatrix22Scalar.getSkewX());
+    fMatrix22Scalar.setSkewY(-fMatrix22Scalar.getSkewY());
 
-    SkScalar sx = A.getScaleX();
-    SkScalar sy = A.getScaleY();
-    fMatrix22Scalar.reset();
-
-    // In GDI, the hinter is aware of the current transformation
-    // (the transform is in some sense applied before/with the hinting).
-    // The bytecode can then test if it is rotated or stretched and decide
-    // to apply instructions or not.
-    //
-    // FreeType, however, always does the transformation strictly after hinting.
-    // It just sets 'rotated' and 'stretched' to false and only applies the
-    // size before hinting.
-    //
-    // Also, FreeType respects the head::flags::IntegerScaling flag,
-    // (although this is patched out on most major distros)
-    // so it is critical to get the size correct on the request.
-    //
-    // This also gets us the actual closest size on bitmap fonts as well.
-    if (A.getSkewX() || A.getSkewY() || sx < 0 || sy < 0) {
-        // h is where A maps the horizontal baseline.
-        SkPoint h = SkPoint::Make(SK_Scalar1, 0);
-        A.mapPoints(&h, 1);
-
-        // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
-        SkMatrix G;
-        SkComputeGivensRotation(h, &G);
-
-        // GA is the matrix A with rotation removed.
-        SkMatrix GA(G);
-        GA.preConcat(A);
-
-        sx = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
-        sy = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
-
-        // sA is the total matrix A without the text scale.
-        SkMatrix sA(A);
-        sA.preScale(SkScalarInvert(sx), SkScalarInvert(sy)); //remove text size
-
-        fMatrix22Scalar.setScaleX(sA.getScaleX());
-        fMatrix22Scalar.setSkewX(-sA.getSkewX());
-        fMatrix22Scalar.setSkewY(-sA.getSkewY());
-        fMatrix22Scalar.setScaleY(sA.getScaleY());
-    }
-    fScale.set(sx, sy);
-    fScaleX = SkScalarToFixed(sx);
-    fScaleY = SkScalarToFixed(sy);
+    fScaleX = SkScalarToFixed(fScale.fX);
+    fScaleY = SkScalarToFixed(fScale.fY);
     fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX());
     fMatrix22.xy = SkScalarToFixed(fMatrix22Scalar.getSkewX());
     fMatrix22.yx = SkScalarToFixed(fMatrix22Scalar.getSkewY());
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 7c0e3ff..5ea55bd 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -617,55 +617,27 @@
     SetGraphicsMode(fDDC, GM_ADVANCED);
     SetBkMode(fDDC, TRANSPARENT);
 
-    SkPoint h = SkPoint::Make(SK_Scalar1, 0);
-    // A is the total matrix.
-    SkMatrix A;
-    fRec.getSingleMatrix(&A);
-    A.mapPoints(&h, 1);
-
-    // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
-    SkMatrix G;
-    SkComputeGivensRotation(h, &G);
-
-    // GA is the matrix A with rotation removed.
-    SkMatrix GA(G);
-    GA.preConcat(A);
-
-    // realTextSize is the actual device size we want (as opposed to the size the user requested).
-    // gdiTextSize is the size we request from GDI.
-    // If the scale is negative, this means the matrix will do the flip anyway.
-    SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
-    SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize);
-    if (gdiTextSize == 0) {
-        gdiTextSize = SK_Scalar1;
-    }
-
-    // When not hinting, remove only the gdiTextSize scale which will be applied by GDI.
     // When GDI hinting, remove the entire Y scale to prevent 'subpixel' metrics.
-    SkScalar scale = (fRec.getHinting() == SkPaint::kNo_Hinting ||
-                      fRec.getHinting() == SkPaint::kSlight_Hinting)
-                   ? SkScalarInvert(gdiTextSize)
-                   : SkScalarInvert(realTextSize);
-
-    // sA is the total matrix A without the textSize (so GDI knows the text size separately).
-    // When this matrix is used with GetGlyphOutline, no further processing is needed.
-    SkMatrix sA(A);
-    sA.preScale(scale, scale); //remove text size
-
-    // GsA is the non-rotational part of A without the text height scale.
-    // This is what is used to find the magnitude of advances.
-    SkMatrix GsA(GA);
-    GsA.preScale(scale, scale); //remove text size, G is rotational so reorders with the scale.
+    // When not hinting, remove only the gdiTextSize scale which will be applied by GDI.
+    SkScalerContextRec::PreMatrixScale scaleConstraints =
+        (fRec.getHinting() == SkPaint::kNo_Hinting || fRec.getHinting() == SkPaint::kSlight_Hinting)
+                   ? SkScalerContextRec::kVerticalInteger_PreMatrixScale
+                   : SkScalerContextRec::kVertical_PreMatrixScale;
+    SkVector scale;
+    SkMatrix sA;
+    SkMatrix GsA;
+    SkMatrix A;
+    fRec.computeMatrices(scaleConstraints, &scale, &sA, &GsA, &fG_inv, &A);
 
     fGsA.eM11 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleX));
     fGsA.eM12 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
     fGsA.eM21 = SkScalarToFIXED(-GsA.get(SkMatrix::kMSkewX));
     fGsA.eM22 = SkScalarToFIXED(GsA.get(SkMatrix::kMScaleY));
 
-    // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
-    fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
-                  -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
-                  G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
+    SkScalar gdiTextSize = scale.fY;
+    if (gdiTextSize == 0) {
+        gdiTextSize = SK_Scalar1;
+    }
 
     LOGFONT lf = typeface->fLogFont;
     lf.lfHeight = -SkScalarTruncToInt(gdiTextSize);
diff --git a/src/ports/SkScalerContext_win_dw.cpp b/src/ports/SkScalerContext_win_dw.cpp
index f3f371c..1e10e17 100644
--- a/src/ports/SkScalerContext_win_dw.cpp
+++ b/src/ports/SkScalerContext_win_dw.cpp
@@ -210,26 +210,29 @@
     // Also, rotated glyphs should have the same absolute advance widths as
     // horizontal glyphs and the subpixel flag should not affect glyph shapes.
 
-    // A is the total matrix.
-    SkMatrix A;
-    fRec.getSingleMatrix(&A);
+    SkVector scale;
+    SkMatrix GsA;
+    fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
+                         &scale, &fSkXform, &GsA, &fG_inv);
 
-    // h is where A maps the horizontal baseline.
-    SkPoint h = SkPoint::Make(SK_Scalar1, 0);
-    A.mapPoints(&h, 1);
+    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
+    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
+    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
+    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
+    fXform.dx = 0;
+    fXform.dy = 0;
 
-    // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
-    SkMatrix G;
-    SkComputeGivensRotation(h, &G);
-
-    // GA is the matrix A with rotation removed.
-    SkMatrix GA(G);
-    GA.preConcat(A);
+    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
+    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
+    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
+    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
+    fGsA.dx = 0;
+    fGsA.dy = 0;
 
     // realTextSize is the actual device size we want (as opposed to the size the user requested).
     // gdiTextSize is the size we request when GDI compatible.
     // If the scale is negative, this means the matrix will do the flip anyway.
-    SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
+    const SkScalar realTextSize = scale.fY;
     // Due to floating point math, the lower bits are suspect. Round carefully.
     SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
     if (gdiTextSize == 0) {
@@ -300,36 +303,6 @@
         fTextSizeMeasure = realTextSize;
         fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
     }
-
-    // Remove the realTextSize, as that is the text height scale currently in A.
-    SkScalar scale = SkScalarInvert(realTextSize);
-
-    // fSkXform is the total matrix A without the text height scale.
-    fSkXform = A;
-    fSkXform.preScale(scale, scale); //remove the text height scale.
-
-    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
-    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
-    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
-    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
-    fXform.dx = 0;
-    fXform.dy = 0;
-
-    // GsA is the non-rotational part of A without the text height scale.
-    SkMatrix GsA(GA);
-    GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale.
-
-    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
-    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
-    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
-    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
-    fGsA.dx = 0;
-    fGsA.dy = 0;
-
-    // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
-    fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
-                  -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
-                  G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
 }
 
 SkScalerContext_DW::~SkScalerContext_DW() {