Fix size of rotated text with FreeType.

This generalizes and shares the solution found for a similar issue with
GDI. The issue is that the text size is applied early, and the rest of
the transformation late. This allows us to isolate and independently
control the text size from the rest of the transformation.

R=reed@google.com

Review URL: https://codereview.chromium.org/213153006

git-svn-id: http://skia.googlecode.com/svn/trunk@14201 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 070d18a..5b3d9ea 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -81,6 +81,8 @@
         '../src/utils/SkGatherPixelRefsAndRects.h',
         '../src/utils/SkInterpolator.cpp',
         '../src/utils/SkLayer.cpp',
+        '../src/utils/SkMatrix22.cpp',
+        '../src/utils/SkMatrix22.h',
         '../src/utils/SkMatrix44.cpp',
         '../src/utils/SkMD5.cpp',
         '../src/utils/SkMD5.h',
diff --git a/include/core/SkFloatingPoint.h b/include/core/SkFloatingPoint.h
index 7dfa9d8..95c28bc 100644
--- a/include/core/SkFloatingPoint.h
+++ b/include/core/SkFloatingPoint.h
@@ -14,6 +14,12 @@
 
 #include <math.h>
 #include <float.h>
+
+// For _POSIX_VERSION
+#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
+#include <unistd.h>
+#endif
+
 #include "SkFloatBits.h"
 
 // C++98 cmath std::pow seems to be the earliest portable way to get float pow.
@@ -24,9 +30,24 @@
 }
 
 static inline float sk_float_copysign(float x, float y) {
+// c++11 contains a 'float copysign(float, float)' function in <cmath>.
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+    return copysign(x, y);
+
+// Posix has demanded 'float copysignf(float, float)' (from C99) since Issue 6.
+#elif defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
+    return copysignf(x, y);
+
+// Visual studio prior to 13 only has 'double _copysign(double, double)'.
+#elif defined(_MSC_VER)
+    return _copysign(x, y);
+
+// Otherwise convert to bits and extract sign.
+#else
     int32_t xbits = SkFloat2Bits(x);
     int32_t ybits = SkFloat2Bits(y);
     return SkBits2Float((xbits & 0x7FFFFFFF) | (ybits & 0x80000000));
+#endif
 }
 
 #ifdef SK_BUILD_FOR_WINCE
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 97caab4..e1d62c0 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -18,6 +18,7 @@
 #include "SkGlyph.h"
 #include "SkMask.h"
 #include "SkMaskGamma.h"
+#include "SkMatrix22.h"
 #include "SkOTUtils.h"
 #include "SkOnce.h"
 #include "SkScalerContext.h"
@@ -43,6 +44,8 @@
 #include FT_LCD_FILTER_H
 #endif
 
+// Defined in FreeType 2.3.8 and later.
+// This is a silly build time check, we would need a runtime check if we really cared.
 #ifdef   FT_ADVANCES_H
 #include FT_ADVANCES_H
 #endif
@@ -693,9 +696,6 @@
 
 ///////////////////////////////////////////////////////////////////////////
 
-#define BLACK_LUMINANCE_LIMIT   0x40
-#define WHITE_LUMINANCE_LIMIT   0xA0
-
 static bool bothZero(SkScalar a, SkScalar b) {
     return 0 == a && 0 == b;
 }
@@ -849,46 +849,34 @@
     }
     fFace = fFaceRec->fFace;
 
-    // compute our factors from the record
+    // A is the total matrix.
+    SkMatrix A;
+    fRec.getSingleMatrix(&A);
 
-    SkMatrix    m;
-
-    fRec.getSingleMatrix(&m);
-
-#ifdef DUMP_STRIKE_CREATION
-    SkString     keyString;
-    SkFontHost::GetDescriptorKeyString(desc, &keyString);
-    printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize),
-           SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
-           SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
-           SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
-           fRec.getHinting(), fRec.fMaskFormat, keyString.c_str());
-#endif
-
-    //  now compute our scale factors
-    SkScalar    sx = m.getScaleX();
-    SkScalar    sy = m.getScaleY();
-
+    SkScalar sx = A.getScaleX();
+    SkScalar sy = A.getScaleY();
     fMatrix22Scalar.reset();
 
-    if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) {
+//#define SK_IGNORE_FREETYPE_ROTATION_FIX
+#ifdef SK_IGNORE_FREETYPE_ROTATION_FIX
+    if (A.getSkewX() || A.getSkewY() || sx < 0 || sy < 0) {
         // sort of give up on hinting
-        sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX()));
-        sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy));
+        sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(A.getSkewX()));
+        sy = SkMaxScalar(SkScalarAbs(A.getSkewY()), SkScalarAbs(sy));
         sx = sy = SkScalarAve(sx, sy);
 
         SkScalar inv = SkScalarInvert(sx);
 
         // flip the skew elements to go from our Y-down system to FreeType's
-        fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv));
-        fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv));
-        fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv));
-        fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv));
+        fMatrix22.xx = SkScalarToFixed(SkScalarMul(A.getScaleX(), inv));
+        fMatrix22.xy = -SkScalarToFixed(SkScalarMul(A.getSkewX(), inv));
+        fMatrix22.yx = -SkScalarToFixed(SkScalarMul(A.getSkewY(), inv));
+        fMatrix22.yy = SkScalarToFixed(SkScalarMul(A.getScaleY(), inv));
 
-        fMatrix22Scalar.setScaleX(SkScalarMul(m.getScaleX(), inv));
-        fMatrix22Scalar.setSkewX(-SkScalarMul(m.getSkewX(), inv));
-        fMatrix22Scalar.setSkewY(-SkScalarMul(m.getSkewY(), inv));
-        fMatrix22Scalar.setScaleY(SkScalarMul(m.getScaleY(), inv));
+        fMatrix22Scalar.setScaleX(SkScalarMul(A.getScaleX(), inv));
+        fMatrix22Scalar.setSkewX(-SkScalarMul(A.getSkewX(), inv));
+        fMatrix22Scalar.setSkewY(-SkScalarMul(A.getSkewY(), inv));
+        fMatrix22Scalar.setScaleY(SkScalarMul(A.getScaleY(), inv));
     } else {
         fMatrix22.xx = fMatrix22.yy = SK_Fixed1;
         fMatrix22.xy = fMatrix22.yx = 0;
@@ -896,6 +884,54 @@
     fScale.set(sx, sy);
     fScaleX = SkScalarToFixed(sx);
     fScaleY = SkScalarToFixed(sy);
+#else
+    // 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);
+    fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX());
+    fMatrix22.xy = SkScalarToFixed(fMatrix22Scalar.getSkewX());
+    fMatrix22.yx = SkScalarToFixed(fMatrix22Scalar.getSkewY());
+    fMatrix22.yy = SkScalarToFixed(fMatrix22Scalar.getScaleY());
+#endif
 
     fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
 
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index 8ed3b66..47a29c0 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -16,6 +16,7 @@
 #include "SkGlyph.h"
 #include "SkHRESULT.h"
 #include "SkMaskGamma.h"
+#include "SkMatrix22.h"
 #include "SkOTTable_maxp.h"
 #include "SkOTTable_name.h"
 #include "SkOTUtils.h"
@@ -636,34 +637,9 @@
     fRec.getSingleMatrix(&A);
     A.mapPoints(&h, 1);
 
-    // Find the Given's matrix [[c, -s],[s, c]] which rotates the baseline vector h
-    // (where the baseline is mapped to) to the positive horizontal axis.
-    const SkScalar& a = h.fX;
-    const SkScalar& b = h.fY;
-    SkScalar c, s;
-    if (0 == b) {
-        c = SkDoubleToScalar(_copysign(SK_Scalar1, a));
-        s = 0;
-    } else if (0 == a) {
-        c = 0;
-        s = SkDoubleToScalar(-_copysign(SK_Scalar1, b));
-    } else if (SkScalarAbs(b) > SkScalarAbs(a)) {
-        SkScalar t = a / b;
-        SkScalar u = SkDoubleToScalar(_copysign(SkScalarSqrt(SK_Scalar1 + t*t), b));
-        s = -1 / u;
-        c = -s * t;
-    } else {
-        SkScalar t = b / a;
-        SkScalar u = SkDoubleToScalar(_copysign(SkScalarSqrt(SK_Scalar1 + t*t), a));
-        c = 1 / u;
-        s = -c * t;
-    }
-
-    // G is the Given's Matrix for A (rotational matrix such that GA[0][1] == 0).
+    // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
     SkMatrix G;
-    G.setAll(c, -s, 0,
-             s,  c, 0,
-             0,  0, SkScalarToPersp(SK_Scalar1));
+    SkComputeGivensRotation(h, &G);
 
     // GA is the matrix A with rotation removed.
     SkMatrix GA(G);
diff --git a/src/utils/SkMatrix22.cpp b/src/utils/SkMatrix22.cpp
new file mode 100644
index 0000000..107e123
--- /dev/null
+++ b/src/utils/SkMatrix22.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMatrix.h"
+#include "SkPoint.h"
+#include "SkScalar.h"
+
+void SkComputeGivensRotation(const SkVector& h, SkMatrix* G) {
+    const SkScalar& a = h.fX;
+    const SkScalar& b = h.fY;
+    SkScalar c, s;
+    if (0 == b) {
+        c = SkScalarCopySign(SK_Scalar1, a);
+        s = 0;
+        //r = SkScalarAbs(a);
+    } else if (0 == a) {
+        c = 0;
+        s = -SkScalarCopySign(SK_Scalar1, b);
+        //r = SkScalarAbs(b);
+    } else if (SkScalarAbs(b) > SkScalarAbs(a)) {
+        SkScalar t = a / b;
+        SkScalar u = SkScalarCopySign(SkScalarSqrt(SK_Scalar1 + t*t), b);
+        s = -SK_Scalar1 / u;
+        c = -s * t;
+        //r = b * u;
+    } else {
+        SkScalar t = b / a;
+        SkScalar u = SkScalarCopySign(SkScalarSqrt(SK_Scalar1 + t*t), a);
+        c = SK_Scalar1 / u;
+        s = -c * t;
+        //r = a * u;
+    }
+
+    G->setSinCos(s, c);
+}
diff --git a/src/utils/SkMatrix22.h b/src/utils/SkMatrix22.h
new file mode 100644
index 0000000..d0f4ed3
--- /dev/null
+++ b/src/utils/SkMatrix22.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPoint.h"
+
+class SkMatrix;
+
+/** Find the Givens matrix G, which is the rotational matrix
+ *  that rotates the vector h to the positive hoizontal axis.
+ *  G * h = [hypot(h), 0]
+ *
+ *  This is equivalent to
+ *
+ *  SkScalar r = h.length();
+ *  SkScalar r_inv = r ? SkScalarInvert(r) : 0;
+ *  h.scale(r_inv);
+ *  G->setSinCos(-h.fY, h.fX);
+ *
+ *  but has better numerical stability by using (partial) hypot,
+ *  and saves a multiply by not computing r.
+ */
+void SkComputeGivensRotation(const SkVector& h, SkMatrix* G);