Subpixel glyph rendering support.

This patch adds support for rendering subpixel glyphs (using
Freetype). In order to control this rendering see
SkPaint::setLCDRenderText in SkPaint.h.

To setup the LCD mode, see SkFontHost::SetSubpixelOrientation and
SkFontHost::SetSubpixelOrder in SkFontHost.h.

This patch also adds more fine grained control over hinting (again,
only for Freetype currently). One can now control the hinting with
SkPaint::setHinting.


git-svn-id: http://skia.googlecode.com/svn/trunk@275 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index b30aba5..5367439 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -15,6 +15,7 @@
 ** limitations under the License.
 */
 
+#include "SkColorPriv.h"
 #include "SkScalerContext.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
@@ -32,6 +33,11 @@
 #include FT_OUTLINE_H
 #include FT_SIZES_H
 #include FT_TRUETYPE_TABLES_H
+
+#if defined(SK_BUILD_SUBPIXEL)
+#include FT_LCD_FILTER_H
+#endif
+
 #ifdef   FT_ADVANCES_H
 #include FT_ADVANCES_H
 #endif
@@ -43,6 +49,7 @@
 #include <freetype/ftsizes.h>
 #include <freetype/tttables.h>
 #include <freetype/ftadvanc.h>
+#include <freetype/ftlcdfil.h>
 #endif
 
 //#define ENABLE_GLYPH_SPEW     // for tracing calls
@@ -69,6 +76,21 @@
 
 /////////////////////////////////////////////////////////////////////////
 
+static bool
+InitFreetype() {
+    FT_Error err = FT_Init_FreeType(&gFTLibrary);
+    if (err)
+        return false;
+
+#if defined(SK_BUILD_SUBPIXEL)
+    // Setup LCD filtering. This reduces colour fringes for LCD rendered
+    // glyphs.
+    err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
+#endif
+
+    return true;
+}
+
 class SkScalerContext_FreeType : public SkScalerContext {
 public:
     SkScalerContext_FreeType(const SkDescriptor* desc);
@@ -247,9 +269,8 @@
     FT_Error    err;
 
     if (gFTCount == 0) {
-        err = FT_Init_FreeType(&gFTLibrary);
-//        SkDEBUGF(("FT_Init_FreeType returned %d\n", err));
-        SkASSERT(err == 0);
+        const bool success = InitFreetype();
+        SkASSERT(success);
     }
     ++gFTCount;
 
@@ -275,7 +296,7 @@
            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.fHints, fRec.fMaskFormat, keyString.c_str());
+           fRec.getHinting(), fRec.fMaskFormat, keyString.c_str());
 #endif
 
     //  now compute our scale factors
@@ -305,42 +326,27 @@
 
     // compute the flags we send to Load_Glyph
     {
-        uint32_t flags = FT_LOAD_DEFAULT;
-        uint32_t render_flags = FT_LOAD_TARGET_NORMAL;
+        FT_Int32 hintingFlags = FT_LOAD_DEFAULT;
 
-        // we force autohinting at the moment
-
-        switch (fRec.fHints) {
-        case kNo_Hints:
-            flags |= FT_LOAD_NO_HINTING;
+        switch (fRec.getHinting()) {
+        case SkPaint::kNo_Hinting:
+            hintingFlags = FT_LOAD_NO_HINTING;
             break;
-        case kSubpixel_Hints:
-            flags |= FT_LOAD_FORCE_AUTOHINT;
-            render_flags = FT_LOAD_TARGET_LIGHT;
+        case SkPaint::kSlight_Hinting:
+            hintingFlags = FT_LOAD_TARGET_LIGHT;  // This implies FORCE_AUTOHINT
             break;
-        case kNormal_Hints:
-#ifdef ANDROID
-            // The following line disables the font's hinting tables. For
-            // Chromium we want font hinting on so that we can generate
-            // baselines that look at little like Firefox. It's expected that
-            // Mike Reed will rework this code sometime soon so we don't wish
-            // to make more extensive changes.
-            flags |= FT_LOAD_FORCE_AUTOHINT;
-            /*  Switch to light hinting (vertical only) to address some chars
-                that behaved poorly with NORMAL. In the future we could consider
-                making this choice exposed at runtime to the caller.
-            */
-            render_flags = FT_LOAD_TARGET_LIGHT;
-#endif
+        case SkPaint::kNormal_Hinting:
+            hintingFlags |= FT_LOAD_TARGET_NORMAL;
+            break;
+        case SkPaint::kFull_Hinting:
+            if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat)
+                hintingFlags = FT_LOAD_TARGET_LCD;
+            else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat)
+                hintingFlags = FT_LOAD_TARGET_LCD_V;
             break;
         }
 
-        if (SkMask::kBW_Format == fRec.fMaskFormat)
-            render_flags = FT_LOAD_TARGET_MONO;
-        else if (SkMask::kLCD_Format == fRec.fMaskFormat)
-            render_flags = FT_LOAD_TARGET_LCD;
-
-        fLoadGlyphFlags = flags | render_flags;
+        fLoadGlyphFlags = hintingFlags;
     }
 
     // now create the FT_Size
@@ -433,10 +439,12 @@
 
 static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
     switch (format) {
+        case SkMask::kHorizontalLCD_Format:
+        case SkMask::kVerticalLCD_Format:
+            SkASSERT(!"An LCD format should never be passed here");
+            return FT_PIXEL_MODE_GRAY;
         case SkMask::kBW_Format:
             return FT_PIXEL_MODE_MONO;
-        case SkMask::kLCD_Format:
-            return FT_PIXEL_MODE_LCD;
         case SkMask::kA8_Format:
         default:
             return FT_PIXEL_MODE_GRAY;
@@ -503,7 +511,7 @@
 
         FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
 
-        if (kSubpixel_Hints == fRec.fHints) {
+        if (fRec.fSubpixelPositioning) {
             int dx = glyph->getSubXFixed() >> 10;
             int dy = glyph->getSubYFixed() >> 10;
             // negate dy since freetype-y-goes-up and skia-y-goes-down
@@ -536,7 +544,7 @@
         goto ERROR;
     }
 
-    if (kNormal_Hints == fRec.fHints) {
+    if (!fRec.fSubpixelPositioning) {
         glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
         glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
         if (fRec.fFlags & kDevKernText_Flag) {
@@ -554,6 +562,16 @@
 #endif
 }
 
+#if defined(SK_BUILD_SUBPIXEL)
+namespace skia_freetype_support {
+// extern functions from SkFontHost_FreeType_Subpixel
+extern void CopyFreetypeBitmapToLCDMask(const SkGlyph& dest, const FT_Bitmap& source);
+extern void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bitmap& source);
+}
+
+using namespace skia_freetype_support;
+#endif
+
 void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
     SkAutoMutexAcquire  ac(gFTMutex);
 
@@ -572,6 +590,9 @@
         return;
     }
 
+    const bool lcdRenderMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format ||
+                               fRec.fMaskFormat == SkMask::kVerticalLCD_Format;
+
     switch ( fFace->glyph->format ) {
         case FT_GLYPH_FORMAT_OUTLINE: {
             FT_Outline* outline = &fFace->glyph->outline;
@@ -579,7 +600,7 @@
             FT_Bitmap   target;
 
             int dx = 0, dy = 0;
-            if (kSubpixel_Hints == fRec.fHints) {
+            if (fRec.fSubpixelPositioning) {
                 dx = glyph.getSubXFixed() >> 10;
                 dy = glyph.getSubYFixed() >> 10;
                 // negate dy since freetype-y-goes-up and skia-y-goes-down
@@ -597,6 +618,23 @@
             FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
                                           dy - ((bbox.yMin + dy) & ~63));
 
+#if defined(SK_BUILD_SUBPIXEL)
+            if (lcdRenderMode) {
+                // FT_Outline_Get_Bitmap cannot render LCD glyphs. In this case
+                // we have to call FT_Render_Glyph and memcpy the image out.
+                const bool isVertical = fRec.fMaskFormat == SkMask::kVerticalLCD_Format;
+                FT_Render_Mode mode = isVertical ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD;
+                FT_Render_Glyph(fFace->glyph, mode);
+
+                if (isVertical)
+                    CopyFreetypeBitmapToVerticalLCDMask(glyph, fFace->glyph->bitmap);
+                else
+                    CopyFreetypeBitmapToLCDMask(glyph, fFace->glyph->bitmap);
+
+                break;
+            }
+#endif
+
             target.width = glyph.fWidth;
             target.rows = glyph.fHeight;
             target.pitch = glyph.rowBytes();
@@ -652,6 +690,10 @@
                     dst += glyph.rowBytes();
                 }
             }
+
+            if (lcdRenderMode)
+                glyph.expandA8ToLCD();
+
         } break;
 
     default:
@@ -858,7 +900,7 @@
 */
 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
     FT_Library  library;
-    if (FT_Init_FreeType(&library)) {
+    if (!InitFreetype()) {
         name->set(NULL);
         return SkTypeface::kNormal;
     }
@@ -905,4 +947,3 @@
     FT_Done_FreeType(library);
     return (SkTypeface::Style)style;
 }
-