improve bitmap font support (FreeType only)

This commit improves SkFontHost_FreeType's support for bitmap fonts,
adding a number of features:
- Intelligent bitmap strike selection.
- Inter-strike bitmap font scaling.
- Colour bitmap font support (FreeType 2.5.0+).

BUG=
R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk/src@12607 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/ports/SkFontConfigInterface_direct.cpp b/ports/SkFontConfigInterface_direct.cpp
index f1ac734..13993f1 100644
--- a/ports/SkFontConfigInterface_direct.cpp
+++ b/ports/SkFontConfigInterface_direct.cpp
@@ -330,11 +330,13 @@
 }
 
 static bool valid_pattern(FcPattern* pattern) {
+#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
     FcBool is_scalable;
     if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
         || !is_scalable) {
         return false;
     }
+#endif
 
     // fontconfig can also return fonts which are unreadable
     const char* c_filename = get_name(pattern, FC_FILE);
diff --git a/ports/SkFontHost_FreeType.cpp b/ports/SkFontHost_FreeType.cpp
index fd87a66..745a58a 100644
--- a/ports/SkFontHost_FreeType.cpp
+++ b/ports/SkFontHost_FreeType.cpp
@@ -58,6 +58,21 @@
 #include <freetype/ftsynth.h>
 #endif
 
+// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
+// were introduced in FreeType 2.5.0.
+// The following may be removed once FreeType 2.5.0 is required to build.
+#ifndef FT_LOAD_COLOR
+#    define FT_LOAD_COLOR ( 1L << 20 )
+#    define FT_PIXEL_MODE_BGRA 7
+#endif
+
+// FT_HAS_COLOR and the corresponding FT_FACE_FLAG_COLOR
+// were introduced in FreeType 2.5.1
+// The following may be removed once FreeType 2.5.1 is required to build.
+#ifndef FT_HAS_COLOR
+#    define FT_HAS_COLOR(face) false
+#endif
+
 //#define ENABLE_GLYPH_SPEW     // for tracing calls
 //#define DUMP_STRIKE_CREATION
 
@@ -184,6 +199,7 @@
     SkFaceRec*  fFaceRec;
     FT_Face     fFace;              // reference to shared face in gFaceRecHead
     FT_Size     fFTSize;            // our own copy
+    FT_Int      fStrikeIndex;
     SkFixed     fScaleX, fScaleY;
     FT_Matrix   fMatrix22;
     uint32_t    fLoadGlyphFlags;
@@ -751,6 +767,50 @@
     return true;
 }
 
+static FT_Int chooseBitmapStrike(FT_Face face, SkFixed scaleY) {
+    // early out if face is bad
+    if (face == NULL) {
+        SkDEBUGF(("chooseBitmapStrike aborted due to NULL face\n"));
+        return -1;
+    }
+    // determine target ppem
+    FT_Pos targetPPEM = SkFixedToFDot6(scaleY);
+    // find a bitmap strike equal to or just larger than the requested size
+    FT_Int chosenStrikeIndex = -1;
+    FT_Pos chosenPPEM = 0;
+    for (FT_Int strikeIndex = 0; strikeIndex < face->num_fixed_sizes; ++strikeIndex) {
+        FT_Pos thisPPEM = face->available_sizes[strikeIndex].y_ppem;
+        if (thisPPEM == targetPPEM) {
+            // exact match - our search stops here
+            chosenPPEM = thisPPEM;
+            chosenStrikeIndex = strikeIndex;
+            break;
+        } else if (chosenPPEM < targetPPEM) {
+            // attempt to increase chosenPPEM
+            if (thisPPEM > chosenPPEM) {
+                chosenPPEM = thisPPEM;
+                chosenStrikeIndex = strikeIndex;
+            }
+        } else {
+            // attempt to decrease chosenPPEM, but not below targetPPEM
+            if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) {
+                chosenPPEM = thisPPEM;
+                chosenStrikeIndex = strikeIndex;
+            }
+        }
+    }
+    if (chosenStrikeIndex != -1) {
+        // use the chosen strike
+        FT_Error err = FT_Select_Size(face, chosenStrikeIndex);
+        if (err != 0) {
+            SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x\n", face->family_name,
+                      chosenStrikeIndex, err));
+            chosenStrikeIndex = -1;
+        }
+    }
+    return chosenStrikeIndex;
+}
+
 SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
                                                    const SkDescriptor* desc)
         : SkScalerContext_FreeType_Base(typeface, desc) {
@@ -764,6 +824,7 @@
     ++gFTCount;
 
     // load the font file
+    fStrikeIndex = -1;
     fFTSize = NULL;
     fFace = NULL;
     fFaceRec = ref_ft_face(typeface);
@@ -823,9 +884,9 @@
     fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
 
     // compute the flags we send to Load_Glyph
+    bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
     {
         FT_Int32 loadFlags = FT_LOAD_DEFAULT;
-        bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
 
         if (SkMask::kBW_Format == fRec.fMaskFormat) {
             // See http://code.google.com/p/chromium/issues/detail?id=43252#c24
@@ -883,42 +944,57 @@
             loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
         }
 
+        loadFlags |= FT_LOAD_COLOR;
+
         fLoadGlyphFlags = loadFlags;
-        fDoLinearMetrics = linearMetrics;
     }
 
-    // now create the FT_Size
+    FT_Error err = FT_New_Size(fFace, &fFTSize);
+    if (err != 0) {
+        SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, fFace->family_name));
+        fFace = NULL;
+        return;
+    }
+    err = FT_Activate_Size(fFTSize);
+    if (err != 0) {
+        SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, fScaleY,
+                  err));
+        fFTSize = NULL;
+        return;
+    }
 
-    {
-        FT_Error    err;
-
-        err = FT_New_Size(fFace, &fFTSize);
+    if (FT_IS_SCALABLE(fFace)) {
+        err = FT_Set_Char_Size(fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72);
         if (err != 0) {
-            SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n",
-                        fFaceRec->fFontID, fScaleX, fScaleY, err));
+            SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n",
+                                    fFace, fScaleX, fScaleY,      err));
             fFace = NULL;
             return;
         }
+        FT_Set_Transform(fFace, &fMatrix22, NULL);
+    } else if (FT_HAS_FIXED_SIZES(fFace)) {
+        fStrikeIndex = chooseBitmapStrike(fFace, fScaleY);
+        if (fStrikeIndex == -1) {
+            SkDEBUGF(("no glyphs for font \"%s\" size %f?\n",
+                            fFace->family_name,       SkFixedToScalar(fScaleY)));
+        } else {
+            // FreeType does no provide linear metrics for bitmap fonts.
+            linearMetrics = false;
 
-        err = FT_Activate_Size(fFTSize);
-        if (err != 0) {
-            SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
-                        fFaceRec->fFontID, fScaleX, fScaleY, err));
-            fFTSize = NULL;
+            // FreeType documentation says:
+            // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading.
+            // Bitmap-only fonts ignore this flag.
+            //
+            // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag.
+            // Force this flag off for bitmap only fonts.
+            fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP;
         }
-
-        err = FT_Set_Char_Size( fFace,
-                                SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY),
-                                72, 72);
-        if (err != 0) {
-            SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
-                        fFaceRec->fFontID, fScaleX, fScaleY, err));
-            fFace = NULL;
-            return;
-        }
-
-        FT_Set_Transform( fFace, &fMatrix22, NULL);
+    } else {
+        SkDEBUGF(("unknown kind of font \"%s\" size %f?\n",
+                            fFace->family_name,       SkFixedToScalar(fScaleY)));
     }
+
+    fDoLinearMetrics = linearMetrics;
 }
 
 SkScalerContext_FreeType::~SkScalerContext_FreeType() {
@@ -932,7 +1008,6 @@
         unref_ft_face(fFace);
     }
     if (--gFTCount == 0) {
-//        SkDEBUGF(("FT_Done_FreeType\n"));
         FT_Done_FreeType(gFTLibrary);
         SkDEBUGCODE(gFTLibrary = NULL;)
     }
@@ -942,18 +1017,18 @@
     this face with other context (at different sizes).
 */
 FT_Error SkScalerContext_FreeType::setupSize() {
-    FT_Error    err = FT_Activate_Size(fFTSize);
-
+    FT_Error err = FT_Activate_Size(fFTSize);
     if (err != 0) {
         SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
-                    fFaceRec->fFontID, fScaleX, fScaleY, err));
+                  fFaceRec->fFontID, fScaleX, fScaleY, err));
         fFTSize = NULL;
-    } else {
-        // seems we need to reset this every time (not sure why, but without it
-        // I get random italics from some other fFTSize)
-        FT_Set_Transform( fFace, &fMatrix22, NULL);
+        return err;
     }
-    return err;
+
+    // seems we need to reset this every time (not sure why, but without it
+    // I get random italics from some other fFTSize)
+    FT_Set_Transform(fFace, &fMatrix22, NULL);
+    return 0;
 }
 
 unsigned SkScalerContext_FreeType::generateGlyphCount() {
@@ -1063,6 +1138,17 @@
     }
 }
 
+inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) {
+    glyph.fWidth *= scale;
+    glyph.fHeight *= scale;
+    glyph.fTop *= scale;
+    glyph.fLeft *= scale;
+
+    SkFixed fixedScale = SkScalarToFixed(scale);
+    glyph.fAdvanceX = SkFixedMul(glyph.fAdvanceX, fixedScale);
+    glyph.fAdvanceY = SkFixedMul(glyph.fAdvanceY, fixedScale);
+}
+
 void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
     SkAutoMutexAcquire  ac(gFTMutex);
 
@@ -1087,32 +1173,28 @@
     }
 
     switch ( fFace->glyph->format ) {
-      case FT_GLYPH_FORMAT_OUTLINE: {
-        FT_BBox bbox;
-
+      case FT_GLYPH_FORMAT_OUTLINE:
         if (0 == fFace->glyph->outline.n_contours) {
             glyph->fWidth = 0;
             glyph->fHeight = 0;
             glyph->fTop = 0;
             glyph->fLeft = 0;
-            break;
+        } else {
+            if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+                emboldenOutline(fFace, &fFace->glyph->outline);
+            }
+
+            FT_BBox bbox;
+            getBBoxForCurrentGlyph(glyph, &bbox, true);
+
+            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));
+
+            updateGlyphIfLCD(glyph);
         }
-
-        if (fRec.fFlags & kEmbolden_Flag) {
-            emboldenOutline(fFace, &fFace->glyph->outline);
-        }
-
-        getBBoxForCurrentGlyph(glyph, &bbox, true);
-
-        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));
-
-        updateGlyphIfLCD(glyph);
-
         break;
-      }
 
       case FT_GLYPH_FORMAT_BITMAP:
         if (fRec.fFlags & kEmbolden_Flag) {
@@ -1129,6 +1211,10 @@
             fFace->glyph->bitmap_top  += SkFDot6Floor(vector.y);
         }
 
+        if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
+            glyph->fMaskFormat = SkMask::kARGB32_Format;
+        }
+
         glyph->fWidth   = SkToU16(fFace->glyph->bitmap.width);
         glyph->fHeight  = SkToU16(fFace->glyph->bitmap.rows);
         glyph->fTop     = -SkToS16(fFace->glyph->bitmap_top);
@@ -1163,6 +1249,11 @@
         }
     }
 
+    if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && fScaleY && fFace->size->metrics.y_ppem) {
+        // NOTE: both dimensions are scaled by y_ppem. this is WAI.
+        scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY),
+                                              SkIntToScalar(fFace->size->metrics.y_ppem)));
+    }
 
 #ifdef ENABLE_GLYPH_SPEW
     SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
@@ -1250,77 +1341,104 @@
     }
 
     FT_Face face = fFace;
-    int upem = face->units_per_EM;
-    if (upem <= 0) {
-        goto ERROR;
-    }
-
-    SkPoint pts[6];
-    SkFixed ys[6];
+    SkScalar scaleX = fScale.x();
     SkScalar scaleY = fScale.y();
-    SkScalar mxy = fMatrix22Scalar.getSkewX();
-    SkScalar myy = fMatrix22Scalar.getScaleY();
-    SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem;
-    SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem;
+    SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY;
+    SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY;
 
-    int leading = face->height - (face->ascender + -face->descender);
-    if (leading < 0) {
-        leading = 0;
-    }
-
-    // Try to get the OS/2 table from the font. This contains the specific
-    // average font width metrics which Windows uses.
-    TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
-
-    ys[0] = -face->bbox.yMax;
-    ys[1] = -face->ascender;
-    ys[2] = -face->descender;
-    ys[3] = -face->bbox.yMin;
-    ys[4] = leading;
-    ys[5] = os2 ? os2->xAvgCharWidth : 0;
-
-    SkScalar x_height;
-    if (os2 && os2->sxHeight) {
-        x_height = fScale.x() * os2->sxHeight / upem;
-    } else {
-        const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
-        if (x_glyph) {
-            FT_BBox bbox;
-            FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
-            if (fRec.fFlags & kEmbolden_Flag) {
-                emboldenOutline(fFace, &fFace->glyph->outline);
-            }
-            FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
-            x_height = bbox.yMax / 64.0f;
-        } else {
-            x_height = 0;
+    // fetch units/EM from "head" table if needed (ie for bitmap fonts)
+    SkScalar upem = SkIntToScalar(face->units_per_EM);
+    if (!upem) {
+        TT_Header* ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, ft_sfnt_head);
+        if (ttHeader) {
+            upem = SkIntToScalar(ttHeader->Units_Per_EM);
         }
     }
 
-    // convert upem-y values into scalar points
-    for (int i = 0; i < 6; i++) {
-        SkScalar y = scaleY * ys[i] / upem;
-        pts[i].set(y * mxy, y * myy);
+    // use the os/2 table as a source of reasonable defaults.
+    SkScalar x_height = 0.0f;
+    SkScalar avgCharWidth = 0.0f;
+    TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+    if (os2) {
+        x_height = scaleX * SkIntToScalar(os2->sxHeight) / upem;
+        avgCharWidth = SkIntToScalar(os2->xAvgCharWidth) / upem;
+    }
+
+    // pull from format-specific metrics as needed
+    SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax;
+    if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font
+        ascent = -SkIntToScalar(face->ascender) / upem;
+        descent = -SkIntToScalar(face->descender) / upem;
+        leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem;
+        xmin = SkIntToScalar(face->bbox.xMin) / upem;
+        xmax = SkIntToScalar(face->bbox.xMax) / upem;
+        ymin = -SkIntToScalar(face->bbox.yMin) / upem;
+        ymax = -SkIntToScalar(face->bbox.yMax) / upem;
+        // we may be able to synthesize x_height from outline
+        if (!x_height) {
+            const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
+            if (x_glyph) {
+                FT_BBox bbox;
+                FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
+                if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+                    emboldenOutline(fFace, &fFace->glyph->outline);
+                }
+                FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
+                x_height = SkIntToScalar(bbox.yMax) / 64.0f;
+            }
+        }
+    } else if (fStrikeIndex != -1) { // bitmap strike metrics
+        SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem);
+        SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem);
+        ascent = -SkIntToScalar(face->size->metrics.ascender) / (yppem * 64.0f);
+        descent = -SkIntToScalar(face->size->metrics.descender) / (yppem * 64.0f);
+        leading = (SkIntToScalar(face->size->metrics.height) / (yppem * 64.0f))
+                + ascent - descent;
+        xmin = 0.0f;
+        xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem;
+        ymin = descent + leading;
+        ymax = ascent - descent;
+        if (!x_height) {
+            x_height = -ascent;
+        }
+        if (!avgCharWidth) {
+            avgCharWidth = xmax - xmin;
+        }
+    } else {
+        goto ERROR;
+    }
+
+    // synthesize elements that were not provided by the os/2 table or format-specific metrics
+    if (!x_height) {
+        x_height = -ascent;
+    }
+    if (!avgCharWidth) {
+        avgCharWidth = xmax - xmin;
+    }
+
+    // disallow negative linespacing
+    if (leading < 0.0f) {
+        leading = 0.0f;
     }
 
     if (mx) {
-        mx->fTop = pts[0].fX;
-        mx->fAscent = pts[1].fX;
-        mx->fDescent = pts[2].fX;
-        mx->fBottom = pts[3].fX;
-        mx->fLeading = pts[4].fX;
-        mx->fAvgCharWidth = pts[5].fX;
+        mx->fTop = ymax * mxy;
+        mx->fAscent = ascent * mxy;
+        mx->fDescent = descent * mxy;
+        mx->fBottom = ymin * mxy;
+        mx->fLeading = leading * mxy;
+        mx->fAvgCharWidth = avgCharWidth * mxy;
         mx->fXMin = xmin;
         mx->fXMax = xmax;
         mx->fXHeight = x_height;
     }
     if (my) {
-        my->fTop = pts[0].fY;
-        my->fAscent = pts[1].fY;
-        my->fDescent = pts[2].fY;
-        my->fBottom = pts[3].fY;
-        my->fLeading = pts[4].fY;
-        my->fAvgCharWidth = pts[5].fY;
+        my->fTop = ymax * myy;
+        my->fAscent = ascent * myy;
+        my->fDescent = descent * myy;
+        my->fBottom = ymin * myy;
+        my->fLeading = leading * myy;
+        my->fAvgCharWidth = avgCharWidth * myy;
         my->fXMin = xmin;
         my->fXMax = xmax;
         my->fXHeight = x_height;
diff --git a/ports/SkFontHost_FreeType_common.cpp b/ports/SkFontHost_FreeType_common.cpp
index 2c48684..065a83a 100644
--- a/ports/SkFontHost_FreeType_common.cpp
+++ b/ports/SkFontHost_FreeType_common.cpp
@@ -6,17 +6,32 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
 #include "SkColorPriv.h"
 #include "SkFDot6.h"
 #include "SkFontHost_FreeType_common.h"
 #include "SkPath.h"
 
 #include <ft2build.h>
-#include FT_OUTLINE_H
+#include FT_FREETYPE_H
 #include FT_BITMAP_H
+#include FT_IMAGE_H
+#include FT_OUTLINE_H
 // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
 #include FT_SYNTHESIS_H
 
+// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
+// were introduced in FreeType 2.5.0.
+// The following may be removed once FreeType 2.5.0 is required to build.
+#ifndef FT_LOAD_COLOR
+#    define FT_LOAD_COLOR ( 1L << 20 )
+#    define FT_PIXEL_MODE_BGRA 7
+#endif
+
+//#define SK_SHOW_TEXT_BLIT_COVERAGE
+
 static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
     switch (format) {
         case SkMask::kBW_Format:
@@ -29,13 +44,20 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
-    return SkPackRGB16(r >> 3, g >> 2, b >> 3);
+static uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) {
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
+    r = SkTMax(r, (U8CPU)0x40);
+    g = SkTMax(g, (U8CPU)0x40);
+    b = SkTMax(b, (U8CPU)0x40);
+#endif
+    return SkPack888ToRGB16(r, g, b);
 }
 
 static uint16_t grayToRGB16(U8CPU gray) {
-    SkASSERT(gray <= 255);
-    return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3);
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
+    gray = SkTMax(gray, (U8CPU)0x40);
+#endif
+    return SkPack888ToRGB16(gray, gray, gray);
 }
 
 static int bittst(const uint8_t data[], int bitOffset) {
@@ -44,78 +66,271 @@
     return lowBit & 1;
 }
 
+/**
+ *  Copies a FT_Bitmap into an SkMask with the same dimensions.
+ *
+ *  FT_PIXEL_MODE_MONO
+ *  FT_PIXEL_MODE_GRAY
+ *  FT_PIXEL_MODE_LCD
+ *  FT_PIXEL_MODE_LCD_V
+ */
 template<bool APPLY_PREBLEND>
-static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
-                         int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR,
-                         const uint8_t* tableG, const uint8_t* tableB) {
-    if (lcdIsVert) {
-        SkASSERT(3 * glyph.fHeight == bitmap.rows);
-    } else {
-        SkASSERT(glyph.fHeight == bitmap.rows);
+static void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB)
+{
+    SkASSERT(SkMask::kLCD16_Format == mask.fFormat);
+    if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) {
+        SkASSERT(mask.fBounds.width() == bitmap.width);
+    }
+    if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) {
+        SkASSERT(mask.fBounds.height() == bitmap.rows);
     }
 
-    uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
-    const size_t dstRB = glyph.rowBytes();
-    const int width = glyph.fWidth;
     const uint8_t* src = bitmap.buffer;
+    uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage);
+    const size_t dstRB = mask.fRowBytes;
+
+    const int width = mask.fBounds.width();
+    const int height = mask.fBounds.height();
 
     switch (bitmap.pixel_mode) {
-        case FT_PIXEL_MODE_MONO: {
-            for (int y = 0; y < glyph.fHeight; ++y) {
+        case FT_PIXEL_MODE_MONO:
+            for (int y = height; y --> 0;) {
                 for (int x = 0; x < width; ++x) {
                     dst[x] = -bittst(src, x);
                 }
                 dst = (uint16_t*)((char*)dst + dstRB);
                 src += bitmap.pitch;
             }
-        } break;
-        case FT_PIXEL_MODE_GRAY: {
-            for (int y = 0; y < glyph.fHeight; ++y) {
+            break;
+        case FT_PIXEL_MODE_GRAY:
+            for (int y = height; y --> 0;) {
                 for (int x = 0; x < width; ++x) {
                     dst[x] = grayToRGB16(src[x]);
                 }
                 dst = (uint16_t*)((char*)dst + dstRB);
                 src += bitmap.pitch;
             }
-        } break;
-        default: {
-            SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width));
-            for (int y = 0; y < glyph.fHeight; y++) {
-                if (lcdIsVert) {    // vertical stripes
-                    const uint8_t* srcR = src;
-                    const uint8_t* srcG = srcR + bitmap.pitch;
-                    const uint8_t* srcB = srcG + bitmap.pitch;
-                    if (lcdIsBGR) {
-                        SkTSwap(srcR, srcB);
-                    }
+            break;
+        case FT_PIXEL_MODE_LCD:
+            SkASSERT(3 * mask.fBounds.width() == bitmap.width);
+            for (int y = height; y --> 0;) {
+                const uint8_t* triple = src;
+                if (lcdIsBGR) {
                     for (int x = 0; x < width; x++) {
-                        dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
-                                            sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
-                                            sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
+                        dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
+                                            sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+                                            sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
+                        triple += 3;
                     }
-                    src += 3 * bitmap.pitch;
-                } else {            // horizontal stripes
-                    const uint8_t* triple = src;
-                    if (lcdIsBGR) {
-                        for (int x = 0; x < width; x++) {
-                            dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
-                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
-                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
-                            triple += 3;
-                        }
-                    } else {
-                        for (int x = 0; x < width; x++) {
-                            dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
-                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
-                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
-                            triple += 3;
-                        }
+                } else {
+                    for (int x = 0; x < width; x++) {
+                        dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
+                                            sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+                                            sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
+                        triple += 3;
                     }
-                    src += bitmap.pitch;
                 }
+                src += bitmap.pitch;
                 dst = (uint16_t*)((char*)dst + dstRB);
             }
-        } break;
+            break;
+        case FT_PIXEL_MODE_LCD_V:
+            SkASSERT(3 * mask.fBounds.height() == bitmap.rows);
+            for (int y = height; y --> 0;) {
+                const uint8_t* srcR = src;
+                const uint8_t* srcG = srcR + bitmap.pitch;
+                const uint8_t* srcB = srcG + bitmap.pitch;
+                if (lcdIsBGR) {
+                    SkTSwap(srcR, srcB);
+                }
+                for (int x = 0; x < width; x++) {
+                    dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
+                                        sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
+                                        sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
+                }
+                src += 3 * bitmap.pitch;
+                dst = (uint16_t*)((char*)dst + dstRB);
+            }
+            break;
+        default:
+            SkDEBUGF(("FT_Pixel_Mode %d", bitmap.pixel_mode));
+            SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16");
+            break;
+    }
+}
+
+/**
+ *  Copies a FT_Bitmap into an SkMask with the same dimensions.
+ *
+ *  Yes, No, Never Requested, Never Produced
+ *
+ *                        kBW kA8 k3D kARGB32 kLCD16 kLCD32
+ *  FT_PIXEL_MODE_MONO     Y   Y  NR     N       Y     NR
+ *  FT_PIXEL_MODE_GRAY     N   Y  NR     N       Y     NR
+ *  FT_PIXEL_MODE_GRAY2   NP  NP  NR    NP      NP     NR
+ *  FT_PIXEL_MODE_GRAY4   NP  NP  NR    NP      NP     NR
+ *  FT_PIXEL_MODE_LCD     NP  NP  NR    NP      NP     NR
+ *  FT_PIXEL_MODE_LCD_V   NP  NP  NR    NP      NP     NR
+ *  FT_PIXEL_MODE_BGRA     N   N  NR     Y       N     NR
+ *
+ *  TODO: All of these N need to be Y or otherwise ruled out.
+ */
+static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) {
+    SkASSERT(dstMask.fBounds.width() == srcFTBitmap.width);
+    SkASSERT(dstMask.fBounds.height() == srcFTBitmap.rows);
+
+    const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer);
+    const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode);
+    // FT_Bitmap::pitch is an int and allowed to be negative.
+    const int srcPitch = srcFTBitmap.pitch;
+    const size_t srcRowBytes = SkTAbs(srcPitch);
+
+    uint8_t* dst = dstMask.fImage;
+    const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat);
+    const size_t dstRowBytes = dstMask.fRowBytes;
+
+    const size_t width = srcFTBitmap.width;
+    const size_t height = srcFTBitmap.rows;
+
+    if (SkMask::kLCD16_Format == dstFormat) {
+        copyFT2LCD16<false>(srcFTBitmap, dstMask, false, NULL, NULL, NULL);
+        return;
+    }
+
+    if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) ||
+        (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat))
+    {
+        size_t commonRowBytes = SkTMin(srcRowBytes, dstRowBytes);
+        for (size_t y = height; y --> 0;) {
+            memcpy(dst, src, commonRowBytes);
+            src += srcPitch;
+            dst += dstRowBytes;
+        }
+    } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) {
+        for (size_t y = height; y --> 0;) {
+            uint8_t byte = 0;
+            int bits = 0;
+            const uint8_t* src_row = src;
+            uint8_t* dst_row = dst;
+            for (size_t x = width; x --> 0;) {
+                if (0 == bits) {
+                    byte = *src_row++;
+                    bits = 8;
+                }
+                *dst_row++ = byte & 0x80 ? 0xff : 0x00;
+                bits--;
+                byte <<= 1;
+            }
+            src += srcPitch;
+            dst += dstRowBytes;
+        }
+    } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) {
+        // FT_PIXEL_MODE_BGRA is pre-multiplied.
+        for (size_t y = height; y --> 0;) {
+            const uint8_t* src_row = src;
+            SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst);
+            for (size_t x = 0; x < width; ++x) {
+                uint8_t b = *src_row++;
+                uint8_t g = *src_row++;
+                uint8_t r = *src_row++;
+                uint8_t a = *src_row++;
+                *dst_row++ = SkPackARGB32(a, r, g, b);
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE
+                *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40);
+#endif
+            }
+            src += srcPitch;
+            dst += dstRowBytes;
+        }
+    } else {
+        SkDEBUGF(("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat));
+        SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format");
+    }
+}
+
+static inline int convert_8_to_1(unsigned byte) {
+    SkASSERT(byte <= 0xFF);
+    // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better.
+    return (byte >> 6) != 0;
+}
+
+static uint8_t pack_8_to_1(const uint8_t alpha[8]) {
+    unsigned bits = 0;
+    for (int i = 0; i < 8; ++i) {
+        bits <<= 1;
+        bits |= convert_8_to_1(alpha[i]);
+    }
+    return SkToU8(bits);
+}
+
+static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) {
+    const int height = mask.fBounds.height();
+    const int width = mask.fBounds.width();
+    const int octs = width >> 3;
+    const int leftOverBits = width & 7;
+
+    uint8_t* dst = mask.fImage;
+    const int dstPad = mask.fRowBytes - SkAlign8(width)/8;
+    SkASSERT(dstPad >= 0);
+
+    const int srcPad = srcRB - width;
+    SkASSERT(srcPad >= 0);
+
+    for (int y = 0; y < height; ++y) {
+        for (int i = 0; i < octs; ++i) {
+            *dst++ = pack_8_to_1(src);
+            src += 8;
+        }
+        if (leftOverBits > 0) {
+            unsigned bits = 0;
+            int shift = 7;
+            for (int i = 0; i < leftOverBits; ++i, --shift) {
+                bits |= convert_8_to_1(*src++) << shift;
+            }
+            *dst++ = bits;
+        }
+        src += srcPad;
+        dst += dstPad;
+    }
+}
+
+inline SkMask::Format SkMaskFormat_for_SkBitmapConfig(SkBitmap::Config config) {
+    switch (config) {
+        case SkBitmap::kA8_Config:
+            return SkMask::kA8_Format;
+        case SkBitmap::kARGB_8888_Config:
+            return SkMask::kARGB32_Format;
+        default:
+            SkDEBUGFAIL("unsupported SkBitmap::Config");
+            return SkMask::kA8_Format;
+    }
+}
+
+inline SkBitmap::Config SkBitmapConfig_for_FTPixelMode(FT_Pixel_Mode pixel_mode) {
+    switch (pixel_mode) {
+        case FT_PIXEL_MODE_MONO:
+        case FT_PIXEL_MODE_GRAY:
+            return SkBitmap::kA8_Config;
+        case FT_PIXEL_MODE_BGRA:
+            return SkBitmap::kARGB_8888_Config;
+        default:
+            SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
+            return SkBitmap::kA8_Config;
+    }
+}
+
+inline SkBitmap::Config SkBitmapConfig_for_SkMaskFormat(SkMask::Format format) {
+    switch (format) {
+        case SkMask::kBW_Format:
+        case SkMask::kA8_Format:
+        case SkMask::kLCD16_Format:
+            return SkBitmap::kA8_Config;
+        case SkMask::kARGB32_Format:
+            return SkBitmap::kARGB_8888_Config;
+        default:
+            SkDEBUGFAIL("unsupported destination SkBitmap::Config");
+            return SkBitmap::kA8_Config;
     }
 }
 
@@ -129,7 +344,8 @@
             FT_BBox     bbox;
             FT_Bitmap   target;
 
-            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) {
+            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&
+                !(face->style_flags & FT_STYLE_FLAG_BOLD)) {
                 emboldenOutline(face, outline);
             }
 
@@ -154,11 +370,13 @@
 
             if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
                 FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);
+                SkMask mask;
+                glyph.toMask(&mask);
                 if (fPreBlend.isApplicable()) {
-                    copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert,
+                    copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR,
                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
                 } else {
-                    copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
+                    copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR,
                                         fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
                 }
             } else {
@@ -166,8 +384,7 @@
                 target.rows = glyph.fHeight;
                 target.pitch = glyph.rowBytes();
                 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
-                target.pixel_mode = compute_pixel_mode(
-                                                (SkMask::Format)fRec.fMaskFormat);
+                target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat);
                 target.num_grays = 256;
 
                 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
@@ -176,71 +393,106 @@
         } break;
 
         case FT_GLYPH_FORMAT_BITMAP: {
-            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) {
+            FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
+            SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
+
+            // Assume that the other formats do not exist.
+            SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||
+                     FT_PIXEL_MODE_GRAY == pixel_mode ||
+                     FT_PIXEL_MODE_BGRA == pixel_mode);
+
+            // These are the only formats this ScalerContext should request.
+            SkASSERT(SkMask::kBW_Format == maskFormat ||
+                     SkMask::kA8_Format == maskFormat ||
+                     SkMask::kARGB32_Format == maskFormat ||
+                     SkMask::kLCD16_Format == maskFormat);
+
+            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&
+                !(face->style_flags & FT_STYLE_FLAG_BOLD))
+            {
                 FT_GlyphSlot_Own_Bitmap(face->glyph);
-                FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0);
+                FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap,
+                                   kBitmapEmboldenStrength, 0);
             }
-            SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width);
-            SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows);
-            SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top);
-            SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left);
 
-            const uint8_t*  src = (const uint8_t*)face->glyph->bitmap.buffer;
-            uint8_t*        dst = (uint8_t*)glyph.fImage;
+            // If no scaling needed, directly copy glyph bitmap.
+            if (glyph.fWidth == face->glyph->bitmap.width &&
+                glyph.fHeight == face->glyph->bitmap.rows &&
+                glyph.fTop == -face->glyph->bitmap_top &&
+                glyph.fLeft == face->glyph->bitmap_left)
+            {
+                SkMask dstMask;
+                glyph.toMask(&dstMask);
+                copyFTBitmap(face->glyph->bitmap, dstMask);
+                break;
+            }
 
-            if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY ||
-                (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
-                 glyph.fMaskFormat == SkMask::kBW_Format)) {
-                unsigned    srcRowBytes = face->glyph->bitmap.pitch;
-                unsigned    dstRowBytes = glyph.rowBytes();
-                unsigned    minRowBytes = SkMin32(srcRowBytes, dstRowBytes);
-                unsigned    extraRowBytes = dstRowBytes - minRowBytes;
+            // Otherwise, scale the bitmap.
 
-                for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) {
-                    memcpy(dst, src, minRowBytes);
-                    memset(dst + minRowBytes, 0, extraRowBytes);
-                    src += srcRowBytes;
-                    dst += dstRowBytes;
-                }
-            } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
-                       glyph.fMaskFormat == SkMask::kA8_Format) {
-                for (int y = 0; y < face->glyph->bitmap.rows; ++y) {
-                    uint8_t byte = 0;
-                    int bits = 0;
-                    const uint8_t* src_row = src;
-                    uint8_t* dst_row = dst;
+            // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)
+            SkBitmap unscaledBitmap;
+            unscaledBitmap.setConfig(SkBitmapConfig_for_FTPixelMode(pixel_mode),
+                                     face->glyph->bitmap.width, face->glyph->bitmap.rows);
+            unscaledBitmap.allocPixels();
 
-                    for (int x = 0; x < face->glyph->bitmap.width; ++x) {
-                        if (!bits) {
-                            byte = *src_row++;
-                            bits = 8;
-                        }
+            SkMask unscaledBitmapAlias;
+            unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels());
+            unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height());
+            unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes();
+            unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkBitmapConfig(unscaledBitmap.config());
+            copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias);
 
-                        *dst_row++ = byte & 0x80 ? 0xff : 0;
-                        bits--;
-                        byte <<= 1;
-                    }
-
-                    src += face->glyph->bitmap.pitch;
-                    dst += glyph.rowBytes();
-                }
-            } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
-                if (fPreBlend.isApplicable()) {
-                    copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert,
-                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
-                } else {
-                    copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
-                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
-                }
+            // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.
+            // BW requires an A8 target for resizing, which can then be down sampled.
+            // LCD should use a 4x A8 target, which will then be down sampled.
+            // For simplicity, LCD uses A8 and is replicated.
+            int bitmapRowBytes = 0;
+            if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) {
+                bitmapRowBytes = glyph.rowBytes();
+            }
+            SkBitmap dstBitmap;
+            dstBitmap.setConfig(SkBitmapConfig_for_SkMaskFormat(maskFormat),
+                                glyph.fWidth, glyph.fHeight, bitmapRowBytes);
+            if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) {
+                dstBitmap.allocPixels();
             } else {
-                SkDEBUGFAIL("unknown glyph bitmap transform needed");
+                dstBitmap.setPixels(glyph.fImage);
             }
+
+            // Scale unscaledBitmap into dstBitmap.
+            SkCanvas canvas(dstBitmap);
+            canvas.clear(SK_ColorTRANSPARENT);
+            canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width),
+                         SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows));
+            SkPaint paint;
+            paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            canvas.drawBitmap(unscaledBitmap, 0, 0, &paint);
+
+            // If the destination is BW or LCD, convert from A8.
+            if (SkMask::kBW_Format == maskFormat) {
+                // Copy the A8 dstBitmap into the A1 glyph.fImage.
+                SkMask dstMask;
+                glyph.toMask(&dstMask);
+                packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes());
+            } else if (SkMask::kLCD16_Format == maskFormat) {
+                // Copy the A8 dstBitmap into the LCD16 glyph.fImage.
+                uint8_t* src = dstBitmap.getAddr8(0, 0);
+                uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
+                for (int y = dstBitmap.height(); y --> 0;) {
+                    for (int x = 0; x < dstBitmap.width(); ++x) {
+                        dst[x] = grayToRGB16(src[x]);
+                    }
+                    dst = (uint16_t*)((char*)dst + glyph.rowBytes());
+                    src += dstBitmap.rowBytes();
+                }
+            }
+
         } break;
 
-    default:
-        SkDEBUGFAIL("unknown glyph format");
-        memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
-        return;
+        default:
+            SkDEBUGFAIL("unknown glyph format");
+            memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+            return;
     }
 
 // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,