diff options
-rw-r--r-- | gyp/utils.gyp | 2 | ||||
-rw-r--r-- | include/core/SkFloatingPoint.h | 21 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 104 | ||||
-rwxr-xr-x | src/ports/SkFontHost_win.cpp | 30 | ||||
-rw-r--r-- | src/utils/SkMatrix22.cpp | 39 | ||||
-rw-r--r-- | src/utils/SkMatrix22.h | 26 |
6 files changed, 161 insertions, 61 deletions
diff --git a/gyp/utils.gyp b/gyp/utils.gyp index 070d18a07e..5b3d9ea98c 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 7dfa9d8680..95c28bc18a 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_pow(float base, float exp) { } 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 97caab4b6b..e1d62c027e 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 @@ SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics( /////////////////////////////////////////////////////////////////////////// -#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 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, } fFace = fFaceRec->fFace; - // compute our factors from the record - - 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(); + // A is the total matrix. + SkMatrix A; + fRec.getSingleMatrix(&A); + 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)); - - fMatrix22Scalar.setScaleX(SkScalarMul(m.getScaleX(), inv)); - fMatrix22Scalar.setSkewX(-SkScalarMul(m.getSkewX(), inv)); - fMatrix22Scalar.setSkewY(-SkScalarMul(m.getSkewY(), inv)); - fMatrix22Scalar.setScaleY(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(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 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, 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 8ed3b666aa..47a29c0024 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 @@ SkScalerContext_GDI::SkScalerContext_GDI(SkTypeface* rawTypeface, 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 0000000000..107e123a68 --- /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 0000000000..d0f4ed3155 --- /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); |