Bug 1287552 - part 3 - revise SkFontHost_cairo to properly parse Fontconfig options. r?gw280 draft
authorLee Salzman <lsalzman@mozilla.com>
Wed, 20 Jul 2016 15:06:29 -0400
changeset 390167 7e0dfa9f663a098ef2670c3d363a9b51c856aa96
parent 389230 a9a8957855f5d7cee90e0a092a6e96d924e1ba1b
child 390168 bb3a7806c48ca0f4529e8d010de743e11030def6
push id23612
push userbmo:lsalzman@mozilla.com
push dateWed, 20 Jul 2016 19:07:40 +0000
reviewersgw280
bugs1287552
milestone50.0a1
Bug 1287552 - part 3 - revise SkFontHost_cairo to properly parse Fontconfig options. r?gw280 MozReview-Commit-ID: 40REybKbB6P
gfx/skia/generate_mozbuild.py
gfx/skia/moz.build
gfx/skia/skia/include/ports/SkTypeface_cairo.h
gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
gfx/thebes/gfxPlatform.cpp
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -110,16 +110,19 @@ elif CONFIG['CLANG_CL']:
 if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm':
     SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
 
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 
 if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
     DEFINES['SK_SUPPORT_GPU'] = 0
 
+if CONFIG['MOZ_TREE_FREETYPE']:
+    DEFINES['SK_CAN_USE_DLOPEN'] = 0
+
 # Suppress warnings in third-party code.
 if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']:
     CXXFLAGS += [
         '-Wno-deprecated-declarations',
         '-Wno-overloaded-virtual',
         '-Wno-shadow',
         '-Wno-sign-compare',
         '-Wno-unused-function',
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -634,16 +634,19 @@ elif CONFIG['CLANG_CL']:
 if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm':
     SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
 
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 
 if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
     DEFINES['SK_SUPPORT_GPU'] = 0
 
+if CONFIG['MOZ_TREE_FREETYPE']:
+    DEFINES['SK_CAN_USE_DLOPEN'] = 0
+
 # Suppress warnings in third-party code.
 if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']:
     CXXFLAGS += [
         '-Wno-deprecated-declarations',
         '-Wno-overloaded-virtual',
         '-Wno-shadow',
         '-Wno-sign-compare',
         '-Wno-unused-function',
--- a/gfx/skia/skia/include/ports/SkTypeface_cairo.h
+++ b/gfx/skia/skia/include/ports/SkTypeface_cairo.h
@@ -1,11 +1,18 @@
 #ifndef SkTypeface_cairo_DEFINED
 #define SkTypeface_cairo_DEFINED
 
 #include <cairo.h>
+#include <cairo-ft.h>
 
 #include "SkTypeface.h"
 
-SK_API extern SkTypeface* SkCreateTypefaceFromCairoFont(cairo_font_face_t* fontFace, const SkFontStyle& style, bool isFixedWidth);
+SK_API extern void SkInitCairoFT(bool fontHintingEnabled);
+
+SK_API extern SkTypeface* SkCreateTypefaceFromCairoFTFont(cairo_scaled_font_t* scaledFont);
+
+#ifdef CAIRO_HAS_FC_FONT
+SK_API extern SkTypeface* SkCreateTypefaceFromCairoFTFontWithFontconfig(cairo_scaled_font_t* scaledFont, FcPattern* pattern);
+#endif
 
 #endif
 
--- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
@@ -17,39 +17,110 @@
 #include "SkPath.h"
 #include "SkScalerContext.h"
 #include "SkTypefaceCache.h"
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_OUTLINE_H
 
+// for FT_GlyphSlot_Embolden
+#ifdef FT_SYNTHESIS_H
+#include FT_SYNTHESIS_H
+#endif
+
+// for FT_Library_SetLcdFilter
+#ifdef FT_LCD_FILTER_H
+#include FT_LCD_FILTER_H
+#else
+typedef enum FT_LcdFilter_
+{
+    FT_LCD_FILTER_NONE    = 0,
+    FT_LCD_FILTER_DEFAULT = 1,
+    FT_LCD_FILTER_LIGHT   = 2,
+    FT_LCD_FILTER_LEGACY  = 16,
+} FT_LcdFilter;
+#endif
+
+#ifndef SK_CAN_USE_DLOPEN
+#define SK_CAN_USE_DLOPEN 1
+#endif
+#if SK_CAN_USE_DLOPEN
+#include <dlfcn.h>
+#endif
+
 #ifndef SK_FONTHOST_CAIRO_STANDALONE
 #define SK_FONTHOST_CAIRO_STANDALONE 1
 #endif
 
 static cairo_user_data_key_t kSkTypefaceKey;
 
+static bool gFontHintingEnabled = true;
+static FT_Error (*gSetLcdFilter)(FT_Library, FT_LcdFilter) = nullptr;
+static void (*gGlyphSlotEmbolden)(FT_GlyphSlot) = nullptr;
+
+void SkInitCairoFT(bool fontHintingEnabled)
+{
+    gFontHintingEnabled = fontHintingEnabled;
+#if SK_CAN_USE_DLOPEN
+    gSetLcdFilter = (FT_Error (*)(FT_Library, FT_LcdFilter))dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter");
+    gGlyphSlotEmbolden = (void (*)(FT_GlyphSlot))dlsym(RTLD_DEFAULT, "FT_GlyphSlot_Embolden");
+#else
+    gSetLcdFilter = &FT_Library_SetLcdFilter;
+    gGlyphSlotEmbolden = &FT_GlyphSlot_Embolden;
+#endif
+}
+
+#ifndef CAIRO_HAS_FC_FONT
+typedef struct _FcPattern FcPattern;
+#endif
+
+template<> struct SkTUnref<FcPattern> {
+    void operator()(FcPattern* pattern) {
+#ifdef CAIRO_HAS_FC_FONT
+        if (pattern) {
+            FcPatternDestroy(pattern);
+        }
+#endif
+    }
+};
+
 class SkScalerContext_CairoFT : public SkScalerContext_FreeType_Base {
 public:
-    SkScalerContext_CairoFT(SkTypeface* typeface, const SkDescriptor* desc);
+    SkScalerContext_CairoFT(SkTypeface* typeface, const SkDescriptor* desc,
+                            cairo_font_face_t* fontFace, FcPattern* pattern);
     virtual ~SkScalerContext_CairoFT();
 
 protected:
     virtual unsigned generateGlyphCount() override;
     virtual uint16_t generateCharToGlyph(SkUnichar uniChar) override;
     virtual void generateAdvance(SkGlyph* glyph) override;
     virtual void generateMetrics(SkGlyph* glyph) override;
     virtual void generateImage(const SkGlyph& glyph) override;
     virtual void generatePath(const SkGlyph& glyph, SkPath* path) override;
     virtual void generateFontMetrics(SkPaint::FontMetrics* metrics) override;
     virtual SkUnichar generateGlyphToChar(uint16_t glyph) override;
+
 private:
+    bool computeShapeMatrix(const SkMatrix& m);
+    void prepareGlyph(FT_GlyphSlot glyph);
+    void fixVerticalLayoutBearing(FT_GlyphSlot glyph);
+
+#ifdef CAIRO_HAS_FC_FONT
+    void parsePattern(FcPattern* pattern);
+    void resolvePattern(FcPattern* pattern);
+#endif
+
     cairo_scaled_font_t* fScaledFont;
-    uint32_t fLoadGlyphFlags;
+    FT_Int32 fLoadGlyphFlags;
+    FT_LcdFilter fLcdFilter;
+    SkScalar fScaleX;
+    SkScalar fScaleY;
+    FT_Matrix fShapeMatrix;
+    bool fHaveShape;
 };
 
 class CairoLockedFTFace {
 public:
     CairoLockedFTFace(cairo_scaled_font_t* scaledFont)
         : fScaledFont(scaledFont)
         , fFace(cairo_ft_scaled_font_lock_face(scaledFont))
     {}
@@ -64,61 +135,77 @@ public:
         return fFace;
     }
 
 private:
     cairo_scaled_font_t* fScaledFont;
     FT_Face fFace;
 };
 
+template<typename T> static bool isLCD(const T& rec) {
+    return SkMask::kLCD16_Format == rec.fMaskFormat;
+}
+
 static bool bothZero(SkScalar a, SkScalar b) {
     return 0 == a && 0 == b;
 }
 
 // returns false if there is any non-90-rotation or skew
 static bool isAxisAligned(const SkScalerContext::Rec& rec) {
     return 0 == rec.fPreSkewX &&
            (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
             bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
 }
 
 class SkCairoFTTypeface : public SkTypeface {
 public:
-    static SkTypeface* CreateTypeface(cairo_font_face_t* fontFace, const SkFontStyle& style, bool isFixedWidth) {
-        SkASSERT(fontFace != NULL);
+    static SkTypeface* CreateTypeface(cairo_font_face_t* fontFace, FT_Face face,
+                                      FcPattern* pattern = nullptr) {
+        SkASSERT(fontFace != nullptr);
         SkASSERT(cairo_font_face_get_type(fontFace) == CAIRO_FONT_TYPE_FT);
+        SkASSERT(face != nullptr);
+
+        SkFontStyle style(face->style_flags & FT_STYLE_FLAG_BOLD ?
+                              SkFontStyle::kBold_Weight : SkFontStyle::kNormal_Weight,
+                          SkFontStyle::kNormal_Width,
+                          face->style_flags & FT_STYLE_FLAG_ITALIC ?
+                              SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
+
+        bool isFixedWidth = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
 
         SkFontID newId = SkTypefaceCache::NewFontID();
 
-        return new SkCairoFTTypeface(fontFace, style, newId, isFixedWidth);
+        return new SkCairoFTTypeface(style, newId, isFixedWidth, fontFace, pattern);
     }
 
-    cairo_font_face_t* getFontFace() {
-        return fFontFace;
-    }
-
-    virtual SkStreamAsset* onOpenStream(int*) const override { return NULL; }
+    virtual SkStreamAsset* onOpenStream(int*) const override { return nullptr; }
 
     virtual SkAdvancedTypefaceMetrics*
         onGetAdvancedTypefaceMetrics(PerGlyphInfo,
                                      const uint32_t*, uint32_t) const override
     {
         SkDEBUGCODE(SkDebugf("SkCairoFTTypeface::onGetAdvancedTypefaceMetrics unimplemented\n"));
-        return NULL;
+        return nullptr;
     }
 
     virtual SkScalerContext* onCreateScalerContext(const SkDescriptor* desc) const override
     {
-        return new SkScalerContext_CairoFT(const_cast<SkCairoFTTypeface*>(this), desc);
+        return new SkScalerContext_CairoFT(const_cast<SkCairoFTTypeface*>(this), desc,
+                                           fFontFace, fPattern);
     }
 
     virtual void onFilterRec(SkScalerContextRec* rec) const override
     {
+        // No subpixel AA unless enabled in Fontconfig.
+        if (!fPattern && isLCD(*rec)) {
+            rec->fMaskFormat = SkMask::kA8_Format;
+        }
+
         // rotated text looks bad with hinting, so we disable it as needed
-        if (!isAxisAligned(*rec)) {
+        if (!gFontHintingEnabled || !isAxisAligned(*rec)) {
             rec->setHinting(SkPaint::kNo_Hinting);
         }
     }
 
     virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const override
     {
         SkDEBUGCODE(SkDebugf("SkCairoFTTypeface::onGetFontDescriptor unimplemented\n"));
     }
@@ -135,17 +222,17 @@ public:
 
     virtual int onGetUPEM() const override
     {
         return 0;
     }
 
     virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override
     {
-        return NULL;
+        return nullptr;
     }
 
     virtual void onGetFamilyName(SkString* familyName) const override
     {
         familyName->reset();
     }
 
     virtual int onGetTableTags(SkFontTableTag*) const override
@@ -155,138 +242,336 @@ public:
 
     virtual size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override
     {
         return 0;
     }
 
 private:
 
-    SkCairoFTTypeface(cairo_font_face_t* fontFace, const SkFontStyle& style, SkFontID id, bool isFixedWidth)
+    SkCairoFTTypeface(const SkFontStyle& style, SkFontID id, bool isFixedWidth,
+                      cairo_font_face_t* fontFace, FcPattern* pattern)
         : SkTypeface(style, id, isFixedWidth)
         , fFontFace(fontFace)
+        , fPattern(pattern)
     {
-        cairo_font_face_set_user_data(fFontFace, &kSkTypefaceKey, this, NULL);
+        cairo_font_face_set_user_data(fFontFace, &kSkTypefaceKey, this, nullptr);
         cairo_font_face_reference(fFontFace);
+#ifdef CAIRO_HAS_FC_FONT
+        if (fPattern) {
+            FcPatternReference(fPattern);
+        }
+#endif
     }
 
     ~SkCairoFTTypeface()
     {
-        cairo_font_face_set_user_data(fFontFace, &kSkTypefaceKey, NULL, NULL);
+        cairo_font_face_set_user_data(fFontFace, &kSkTypefaceKey, nullptr, nullptr);
         cairo_font_face_destroy(fFontFace);
     }
 
     cairo_font_face_t* fFontFace;
+    SkAutoTUnref<FcPattern> fPattern;
 };
 
-SkTypeface* SkCreateTypefaceFromCairoFont(cairo_font_face_t* fontFace, const SkFontStyle& style, bool isFixedWidth)
+SkTypeface* SkCreateTypefaceFromCairoFTFontWithFontconfig(cairo_scaled_font_t* scaledFont, FcPattern* pattern)
 {
+    cairo_font_face_t* fontFace = cairo_scaled_font_get_font_face(scaledFont);
+    SkASSERT(cairo_font_face_status(fontFace) == CAIRO_STATUS_SUCCESS);
+
     SkTypeface* typeface = reinterpret_cast<SkTypeface*>(cairo_font_face_get_user_data(fontFace, &kSkTypefaceKey));
-
     if (typeface) {
         typeface->ref();
     } else {
-        typeface = SkCairoFTTypeface::CreateTypeface(fontFace, style, isFixedWidth);
-        SkTypefaceCache::Add(typeface, style);
+        CairoLockedFTFace faceLock(scaledFont);
+        if (FT_Face face = faceLock.getFace()) {
+            typeface = SkCairoFTTypeface::CreateTypeface(fontFace, face, pattern);
+            SkTypefaceCache::Add(typeface, typeface->fontStyle());
+        }
     }
 
     return typeface;
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-static bool isLCD(const SkScalerContext::Rec& rec) {
-    return SkMask::kLCD16_Format == rec.fMaskFormat;
+SkTypeface* SkCreateTypefaceFromCairoFTFont(cairo_scaled_font_t* scaledFont)
+{
+    return SkCreateTypefaceFromCairoFTFontWithFontconfig(scaledFont, nullptr);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-SkScalerContext_CairoFT::SkScalerContext_CairoFT(SkTypeface* typeface, const SkDescriptor* desc)
+SkScalerContext_CairoFT::SkScalerContext_CairoFT(SkTypeface* typeface, const SkDescriptor* desc,
+                                                 cairo_font_face_t* fontFace, FcPattern* pattern)
     : SkScalerContext_FreeType_Base(typeface, desc)
+    , fLcdFilter(FT_LCD_FILTER_NONE)
 {
     SkMatrix matrix;
     fRec.getSingleMatrix(&matrix);
 
-    cairo_font_face_t* fontFace = static_cast<SkCairoFTTypeface*>(typeface)->getFontFace();
-
     cairo_matrix_t fontMatrix, ctMatrix;
     cairo_matrix_init(&fontMatrix, matrix.getScaleX(), matrix.getSkewY(), matrix.getSkewX(), matrix.getScaleY(), 0.0, 0.0);
-    cairo_matrix_init_scale(&ctMatrix, 1.0, 1.0);
+    cairo_matrix_init_identity(&ctMatrix);
+
+    cairo_font_options_t *fontOptions = cairo_font_options_create();
+    fScaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctMatrix, fontOptions);
+    cairo_font_options_destroy(fontOptions);
 
-    // We need to ensure that the font options match for hinting, as generateMetrics()
-    // uses the fScaledFont which uses these font options
-    cairo_font_options_t *fontOptions = cairo_font_options_create();
+    computeShapeMatrix(matrix);
+
+#ifdef CAIRO_HAS_FC_FONT
+    resolvePattern(pattern);
+#endif
 
     FT_Int32 loadFlags = FT_LOAD_DEFAULT;
 
     if (SkMask::kBW_Format == fRec.fMaskFormat) {
-        // See http://code.google.com/p/chromium/issues/detail?id=43252#c24
-        loadFlags = FT_LOAD_TARGET_MONO;
         if (fRec.getHinting() == SkPaint::kNo_Hinting) {
-            cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_NONE);
-            loadFlags = FT_LOAD_NO_HINTING;
+            loadFlags |= FT_LOAD_NO_HINTING;
+        } else {
+            loadFlags = FT_LOAD_TARGET_MONO;
         }
+        loadFlags |= FT_LOAD_MONOCHROME;
     } else {
         switch (fRec.getHinting()) {
         case SkPaint::kNo_Hinting:
-            loadFlags = FT_LOAD_NO_HINTING;
-            cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_NONE);
+            loadFlags |= FT_LOAD_NO_HINTING;
             break;
         case SkPaint::kSlight_Hinting:
             loadFlags = FT_LOAD_TARGET_LIGHT;  // This implies FORCE_AUTOHINT
-            cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_SLIGHT);
             break;
         case SkPaint::kNormal_Hinting:
-            cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_MEDIUM);
             if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
-                loadFlags = FT_LOAD_FORCE_AUTOHINT;
+                loadFlags |= FT_LOAD_FORCE_AUTOHINT;
             }
             break;
         case SkPaint::kFull_Hinting:
-            cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_FULL);
-            if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
-                loadFlags = FT_LOAD_FORCE_AUTOHINT;
-            }
             if (isLCD(fRec)) {
-                if (SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag)) {
+                if (fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag) {
                     loadFlags = FT_LOAD_TARGET_LCD_V;
                 } else {
                     loadFlags = FT_LOAD_TARGET_LCD;
                 }
             }
+            if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
+                loadFlags |= FT_LOAD_FORCE_AUTOHINT;
+            }
             break;
         default:
             SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting());
             break;
         }
     }
 
-    fScaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctMatrix, fontOptions);
-    cairo_font_options_destroy(fontOptions);
+    // Disable autohinting to disable hinting even for "tricky" fonts.
+    if (!gFontHintingEnabled) {
+        loadFlags |= FT_LOAD_NO_AUTOHINT;
+    }
 
     if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) {
         loadFlags |= FT_LOAD_NO_BITMAP;
     }
 
     // Always using FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH to get correct
     // advances, as fontconfig and cairo do.
     // See http://code.google.com/p/skia/issues/detail?id=222.
     loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
+        loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+    }
+
 #ifdef FT_LOAD_COLOR
     loadFlags |= FT_LOAD_COLOR;
 #endif
 
     fLoadGlyphFlags = loadFlags;
 }
 
 SkScalerContext_CairoFT::~SkScalerContext_CairoFT()
 {
     cairo_scaled_font_destroy(fScaledFont);
 }
 
+#ifdef CAIRO_HAS_FC_FONT
+void SkScalerContext_CairoFT::parsePattern(FcPattern* pattern)
+{
+    FcBool antialias, autohint, bitmap, embolden, hinting, vertical;
+
+    if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch && autohint) {
+        fRec.fFlags |= SkScalerContext::kForceAutohinting_Flag;
+    }
+    if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &bitmap) == FcResultMatch && bitmap) {
+        fRec.fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag;
+    }
+    if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch && embolden) {
+        fRec.fFlags |= SkScalerContext::kEmbolden_Flag;
+    }
+    if (FcPatternGetBool(pattern, FC_VERTICAL_LAYOUT, 0, &vertical) == FcResultMatch && vertical) {
+        fRec.fFlags |= SkScalerContext::kVertical_Flag;
+    }
+
+    if (fRec.fMaskFormat != SkMask::kBW_Format &&
+        (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &antialias) != FcResultMatch || antialias)) {
+        int rgba;
+        if (!isLCD(fRec) ||
+            FcPatternGetInteger(pattern, FC_RGBA, 0, &rgba) != FcResultMatch) {
+            rgba = FC_RGBA_UNKNOWN;
+        }
+        switch (rgba) {
+        case FC_RGBA_RGB:
+            break;
+        case FC_RGBA_BGR:
+            fRec.fFlags |= SkScalerContext::kLCD_BGROrder_Flag;
+            break;
+        case FC_RGBA_VRGB:
+            fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag;
+            break;
+        case FC_RGBA_VBGR:
+            fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag |
+                           SkScalerContext::kLCD_BGROrder_Flag;
+            break;
+        default:
+            fRec.fMaskFormat = SkMask::kA8_Format;
+            break;
+        }
+
+        int filter;
+        if (isLCD(fRec)) {
+            if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) != FcResultMatch) {
+                filter = FC_LCD_LEGACY;
+            }
+            switch (filter) {
+            case FC_LCD_NONE:
+                fLcdFilter = FT_LCD_FILTER_NONE;
+                break;
+            case FC_LCD_DEFAULT:
+                fLcdFilter = FT_LCD_FILTER_DEFAULT;
+                break;
+            case FC_LCD_LIGHT:
+                fLcdFilter = FT_LCD_FILTER_LIGHT;
+                break;
+            case FC_LCD_LEGACY:
+            default:
+                fLcdFilter = FT_LCD_FILTER_LEGACY;
+                break;
+            }
+        }
+    } else {
+        fRec.fMaskFormat = SkMask::kBW_Format;
+    }
+
+    if (fRec.getHinting() != SkPaint::kNo_Hinting &&
+        (FcPatternGetBool(pattern, FC_HINTING, 0, &hinting) != FcResultMatch || hinting)) {
+        int hintstyle;
+        if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) {
+            hintstyle = FC_HINT_FULL;
+        }
+        switch (hintstyle) {
+        case FC_HINT_NONE:
+            fRec.setHinting(SkPaint::kNo_Hinting);
+            break;
+        case FC_HINT_SLIGHT:
+            fRec.setHinting(SkPaint::kSlight_Hinting);
+            break;
+        case FC_HINT_MEDIUM:
+        default:
+            fRec.setHinting(SkPaint::kNormal_Hinting);
+            break;
+        case FC_HINT_FULL:
+            fRec.setHinting(SkPaint::kFull_Hinting);
+            break;
+        }
+    }
+}
+
+void SkScalerContext_CairoFT::resolvePattern(FcPattern* pattern)
+{
+    if (!pattern) {
+        return;
+    }
+    SkAutoTUnref<FcPattern> scalePattern(FcPatternDuplicate(pattern));
+    if (scalePattern) {
+        if (FcPatternAddDouble(scalePattern, FC_PIXEL_SIZE, fScaleY) &&
+            FcConfigSubstitute(nullptr, scalePattern, FcMatchPattern)) {
+            FcDefaultSubstitute(scalePattern);
+            FcResult result;
+            SkAutoTUnref<FcPattern> resolved(FcFontMatch(nullptr, scalePattern, &result));
+            if (resolved) {
+                parsePattern(resolved);
+                return;
+            }
+        }
+    }
+    parsePattern(pattern);
+}
+#endif
+
+bool SkScalerContext_CairoFT::computeShapeMatrix(const SkMatrix& m)
+{
+    // Compute a shape matrix compatible with Cairo's _compute_transform.
+    // Finds major/minor scales and uses them to normalize the transform.
+    double scaleX = m.getScaleX();
+    double skewX = m.getSkewX();
+    double skewY = m.getSkewY();
+    double scaleY = m.getScaleY();
+    double det = scaleX * scaleY - skewY * skewX;
+    if (!std::isfinite(det)) {
+        fScaleX = fRec.fTextSize * fRec.fPreScaleX;
+        fScaleY = fRec.fTextSize;
+        fHaveShape = false;
+        return false;
+    }
+    double major = det != 0.0 ? hypot(scaleX, skewY) : 0.0;
+    double minor = major != 0.0 ? fabs(det) / major : 0.0;
+    // Limit scales to be above 1pt.
+    major = SkTMax(major, 1.0);
+    minor = SkTMax(minor, 1.0);
+
+    // If the font is not scalable, then choose the best available size.
+    CairoLockedFTFace faceLock(fScaledFont);
+    FT_Face face = faceLock.getFace();
+    if (!FT_IS_SCALABLE(face)) {
+        double bestDist = DBL_MAX;
+        FT_Int bestSize = -1;
+        for (FT_Int i = 0; i < face->num_fixed_sizes; i++) {
+            // Distance is positive if strike is larger than desired size,
+            // or negative if smaller. If previously a found smaller strike,
+            // then prefer a larger strike. Otherwise, minimize distance.
+            double dist = face->available_sizes[i].y_ppem / 64.0 - minor;
+            if (bestDist < 0 ? dist >= bestDist : fabs(dist) <= bestDist) {
+                bestDist = dist;
+                bestSize = i;
+            }
+        }
+        if (bestSize < 0) {
+            fScaleX = fRec.fTextSize * fRec.fPreScaleX;
+            fScaleY = fRec.fTextSize;
+            fHaveShape = false;
+            return false;
+        }
+        major = face->available_sizes[bestSize].x_ppem / 64.0;
+        minor = face->available_sizes[bestSize].y_ppem / 64.0;
+        fHaveShape = true;
+    } else {
+        fHaveShape = !m.isScaleTranslate();
+    }
+
+    fScaleX = SkDoubleToScalar(major);
+    fScaleY = SkDoubleToScalar(minor);
+
+    if (fHaveShape) {
+        // Normalize the transform and convert to fixed-point.
+        double invScaleX = 65536.0 / major;
+        double invScaleY = 65536.0 / minor;
+        fShapeMatrix.xx = (FT_Fixed)(scaleX * invScaleX);
+        fShapeMatrix.yx = -(FT_Fixed)(skewY * invScaleX);
+        fShapeMatrix.xy = -(FT_Fixed)(skewX * invScaleY);
+        fShapeMatrix.yy = (FT_Fixed)(scaleY * invScaleY);
+    }
+    return true;
+}
+
 unsigned SkScalerContext_CairoFT::generateGlyphCount()
 {
     CairoLockedFTFace faceLock(fScaledFont);
     return faceLock.getFace()->num_glyphs;
 }
 
 uint16_t SkScalerContext_CairoFT::generateCharToGlyph(SkUnichar uniChar)
 {
@@ -294,107 +579,200 @@ uint16_t SkScalerContext_CairoFT::genera
     return SkToU16(FT_Get_Char_Index(faceLock.getFace(), uniChar));
 }
 
 void SkScalerContext_CairoFT::generateAdvance(SkGlyph* glyph)
 {
     generateMetrics(glyph);
 }
 
+void SkScalerContext_CairoFT::prepareGlyph(FT_GlyphSlot glyph)
+{
+    if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&
+        gGlyphSlotEmbolden) {
+        gGlyphSlotEmbolden(glyph);
+    }
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
+        fixVerticalLayoutBearing(glyph);
+    }
+}
+
+void SkScalerContext_CairoFT::fixVerticalLayoutBearing(FT_GlyphSlot glyph)
+{
+    FT_Vector vector;
+    vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX;
+    vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY;
+    if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+        if (fHaveShape) {
+            FT_Vector_Transform(&vector, &fShapeMatrix);
+        }
+        FT_Outline_Translate(&glyph->outline, vector.x, vector.y);
+    } else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
+        glyph->bitmap_left += SkFDot6Floor(vector.x);
+        glyph->bitmap_top  += SkFDot6Floor(vector.y);
+    }
+}
+
 void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph)
 {
-    SkASSERT(fScaledFont != NULL);
+    SkASSERT(fScaledFont != nullptr);
 
     glyph->zeroMetrics();
 
     CairoLockedFTFace faceLock(fScaledFont);
     FT_Face face = faceLock.getFace();
 
     FT_Error err = FT_Load_Glyph( face, glyph->getGlyphID(), fLoadGlyphFlags );
     if (err != 0) {
         return;
     }
 
+    prepareGlyph(face->glyph);
+
     switch (face->glyph->format) {
     case FT_GLYPH_FORMAT_OUTLINE:
         if (!face->glyph->outline.n_contours) {
             break;
         }
+
         FT_BBox bbox;
         FT_Outline_Get_CBox(&face->glyph->outline, &bbox);
         bbox.xMin &= ~63;
         bbox.yMin &= ~63;
         bbox.xMax = (bbox.xMax + 63) & ~63;
         bbox.yMax = (bbox.yMax + 63) & ~63;
         glyph->fWidth  = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin));
         glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin));
         glyph->fTop    = -SkToS16(SkFDot6Floor(bbox.yMax));
         glyph->fLeft   = SkToS16(SkFDot6Floor(bbox.xMin));
+
+        if (isLCD(fRec) &&
+            gSetLcdFilter &&
+            (fLcdFilter == FT_LCD_FILTER_DEFAULT ||
+             fLcdFilter == FT_LCD_FILTER_LIGHT)) {
+            if (fRec.fFlags & kLCD_Vertical_Flag) {
+                glyph->fTop -= 1;
+                glyph->fHeight += 2;
+            } else {
+                glyph->fLeft -= 1;
+                glyph->fWidth += 2;
+            }
+        }
         break;
     case FT_GLYPH_FORMAT_BITMAP:
-        glyph->fWidth  = SkToU16(face->glyph->bitmap.width);
-        glyph->fHeight = SkToU16(face->glyph->bitmap.rows);
-        glyph->fTop    = -SkToS16(face->glyph->bitmap_top);
-        glyph->fLeft   = SkToS16(face->glyph->bitmap_left);
+#ifdef FT_LOAD_COLOR
+        if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
+            glyph->fMaskFormat = SkMask::kARGB32_Format;
+        }
+#endif
+
+        if (isLCD(fRec)) {
+            fRec.fMaskFormat = SkMask::kA8_Format;
+        }
+
+        if (fHaveShape) {
+            // Apply the shape matrix to the glyph's bounding box.
+            SkMatrix matrix;
+            fRec.getSingleMatrix(&matrix);
+            matrix.preScale(SkScalarInvert(fScaleX), SkScalarInvert(fScaleY));
+            SkRect srcRect = SkRect::MakeXYWH(
+                SkIntToScalar(face->glyph->bitmap_left),
+                -SkIntToScalar(face->glyph->bitmap_top),
+                SkIntToScalar(face->glyph->bitmap.width),
+                SkIntToScalar(face->glyph->bitmap.rows));
+            SkRect destRect;
+            matrix.mapRect(&destRect, srcRect);
+            SkIRect glyphRect = destRect.roundOut();
+            glyph->fWidth  = SkToU16(glyphRect.width());
+            glyph->fHeight = SkToU16(glyphRect.height());
+            glyph->fTop    = SkToS16(SkScalarRoundToInt(destRect.fTop));
+            glyph->fLeft   = SkToS16(SkScalarRoundToInt(destRect.fLeft));
+        } else {
+            glyph->fWidth  = SkToU16(face->glyph->bitmap.width);
+            glyph->fHeight = SkToU16(face->glyph->bitmap.rows);
+            glyph->fTop    = -SkToS16(face->glyph->bitmap_top);
+            glyph->fLeft   = SkToS16(face->glyph->bitmap_left);
+        }
         break;
     default:
         SkDEBUGFAIL("unknown glyph format");
         return;
     }
 
-    glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x);
-    glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y);
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
+        glyph->fAdvanceX = -SkFDot6ToFloat(face->glyph->advance.x);
+        glyph->fAdvanceY = SkFDot6ToFloat(face->glyph->advance.y);
+    } else {
+        glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x);
+        glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y);
+    }
 }
 
 void SkScalerContext_CairoFT::generateImage(const SkGlyph& glyph)
 {
-    SkASSERT(fScaledFont != NULL);
+    SkASSERT(fScaledFont != nullptr);
     CairoLockedFTFace faceLock(fScaledFont);
     FT_Face face = faceLock.getFace();
 
     FT_Error err = FT_Load_Glyph(face, glyph.getGlyphID(), fLoadGlyphFlags);
 
     if (err != 0) {
         memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
         return;
     }
 
+    prepareGlyph(face->glyph);
+
+    bool useLcdFilter =
+        face->glyph->format == FT_GLYPH_FORMAT_OUTLINE &&
+        isLCD(glyph) &&
+        gSetLcdFilter;
+    if (useLcdFilter) {
+        gSetLcdFilter(face->glyph->library, fLcdFilter);
+    }
+
     generateGlyphImage(face, glyph);
+
+    if (useLcdFilter) {
+        gSetLcdFilter(face->glyph->library, FT_LCD_FILTER_NONE);
+    }
 }
 
 void SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph, SkPath* path)
 {
-    SkASSERT(fScaledFont != NULL);
+    SkASSERT(fScaledFont != nullptr);
     CairoLockedFTFace faceLock(fScaledFont);
     FT_Face face = faceLock.getFace();
 
     SkASSERT(&glyph && path);
 
     uint32_t flags = fLoadGlyphFlags;
     flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
     flags &= ~FT_LOAD_RENDER;   // don't scan convert (we just want the outline)
 
     FT_Error err = FT_Load_Glyph(face, glyph.getGlyphID(), flags);
 
     if (err != 0) {
         path->reset();
         return;
     }
 
+    prepareGlyph(face->glyph);
+
     generateGlyphPath(face, path);
 }
 
 void SkScalerContext_CairoFT::generateFontMetrics(SkPaint::FontMetrics* metrics)
 {
     SkDEBUGCODE(SkDebugf("SkScalerContext_CairoFT::generateFontMetrics unimplemented\n"));
 }
 
 SkUnichar SkScalerContext_CairoFT::generateGlyphToChar(uint16_t glyph)
 {
-    SkASSERT(fScaledFont != NULL);
+    SkASSERT(fScaledFont != nullptr);
     CairoLockedFTFace faceLock(fScaledFont);
     FT_Face face = faceLock.getFace();
 
     FT_UInt glyphIndex;
     SkUnichar charCode = FT_Get_First_Char(face, &glyphIndex);
     while (glyphIndex != 0) {
         if (glyphIndex == glyph) {
             return charCode;
@@ -406,11 +784,11 @@ SkUnichar SkScalerContext_CairoFT::gener
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkFontMgr.h"
 
 SkFontMgr* SkFontMgr::Factory() {
     // todo
-    return NULL;
+    return nullptr;
 }
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -107,16 +107,19 @@
 #  pragma GCC diagnostic ignored "-Wshadow"
 # endif
 # include "skia/include/core/SkGraphics.h"
 # ifdef USE_SKIA_GPU
 #  include "skia/include/gpu/GrContext.h"
 #  include "skia/include/gpu/gl/GrGLInterface.h"
 #  include "SkiaGLGlue.h"
 # endif
+# ifdef MOZ_ENABLE_FREETYPE
+#  include "skia/include/ports/SkTypeface_cairo.h"
+# endif
 # ifdef __GNUC__
 #  pragma GCC diagnostic pop // -Wshadow
 # endif
 static const uint32_t kDefaultGlyphCacheSize = -1;
 
 #endif
 
 #if !defined(USE_SKIA) || !defined(USE_SKIA_GPU)
@@ -666,16 +669,19 @@ gfxPlatform::Init()
     gPlatform = new gfxAndroidPlatform;
 #else
     #error "No gfxPlatform implementation available"
 #endif
     gPlatform->InitAcceleration();
 
 #ifdef USE_SKIA
     SkGraphics::Init();
+#  ifdef MOZ_ENABLE_FREETYPE
+    SkInitCairoFT(gPlatform->FontHintingEnabled());
+#  endif
 #endif
 
 #ifdef MOZ_GL_DEBUG
     GLContext::StaticInit();
 #endif
 
     InitLayersIPC();