am a223e0da: am 2f12560c: Merge "Enable dynamic effect of GIF animating images in browser"

* commit 'a223e0da1c4693214de7e299e7d4103df917a77f':
  Enable dynamic effect of GIF animating images in browser
diff --git a/Android.mk b/Android.mk
index e293780..28d2d8e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -300,6 +300,7 @@
 	$(LOCAL_PATH)/include/config \
 	$(LOCAL_PATH)/include/effects \
 	$(LOCAL_PATH)/include/images \
+	$(LOCAL_PATH)/include/ports \
 	$(LOCAL_PATH)/include/utils \
 	$(LOCAL_PATH)/include/xml \
 	external/freetype/include \
diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop
deleted file mode 100644
index 6bd7584..0000000
--- a/ThirdPartyProject.prop
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2010 Google Inc. All Rights Reserved.
-#Fri Jul 16 10:03:09 PDT 2010
-currentVersion=Unknown
-version=Unknown
-isNative=true
-name=skia
-keywords=skia
-onDevice=true
-homepage=http\://code.google.com/p/skia/
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h
index 25c9ecb..ace08d8 100644
--- a/include/core/SkFontHost.h
+++ b/include/core/SkFontHost.h
@@ -154,6 +154,15 @@
      */
     static SkFontID NextLogicalFont(SkFontID currFontID, SkFontID origFontID);
 
+#ifdef SK_BUILD_FOR_ANDROID
+    /*
+     * This Android-only version of NextLogicalFont allows us to pass in an
+     * entire Rec structure so that a caller can change fallback behavior
+     */
+    static SkFontID NextLogicalFont(const SkScalerContext::Rec& rec);
+#endif
+
+
     ///////////////////////////////////////////////////////////////////////////
 
     /** Given a filled-out rec, the fonthost may decide to modify it to reflect
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 9e12ece..d2233f0 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -10,6 +10,7 @@
 #ifndef SkPaint_DEFINED
 #define SkPaint_DEFINED
 
+#include "SkTypes.h"
 #include "SkColor.h"
 #include "SkDrawLooper.h"
 #include "SkXfermode.h"
@@ -660,20 +661,34 @@
     void    setTextAlign(Align align);
 
 #ifdef SK_BUILD_FOR_ANDROID
-    /** Return the paint's text locale value.
-        @return the paint's text locale value used for drawing text.
+    /** Return the paint's language value used for drawing text.
+        @return the paint's language value used for drawing text.
     */
-    const SkString& getTextLocale() const { return fTextLocale; }
-
-    /** Set the paint's text locale.
-        @param locale set the paint's locale value for drawing text.
-    */
-    void    setTextLocale(const SkString& locale);
+    const SkLanguage& getLanguage() const { return fLanguage; }
 
     /** Set the paint's language value used for drawing text.
         @param language set the paint's language value for drawing text.
     */
     void setLanguage(const SkLanguage& language);
+
+
+    enum FontVariant {
+       kDefault_Variant, // Currently setting yourself to Default gives you Compact Variant
+       kCompact_Variant,
+       kElegant_Variant,
+       kLast_Variant = kElegant_Variant,
+    };
+
+    /** Return the font variant
+        @return the font variant used by this paint object
+    */
+    FontVariant getFontVariant() const { return fFontVariant; }
+
+
+    /** Set the font variant
+      @param fontVariant set the paint's font variant for choosing fonts
+    */
+    void setFontVariant(FontVariant fontVariant);
 #endif
 
     /** Return the paint's text size.
@@ -870,10 +885,6 @@
                      SkPath* path) const;
 
 #ifdef SK_BUILD_FOR_ANDROID
-
-    enum FontVariant {
-            kElegant_Variant,
-    };
     const SkGlyph& getUnicharMetrics(SkUnichar);
     const SkGlyph& getGlyphMetrics(uint16_t);
     const void* findImage(const SkGlyph&);
@@ -918,7 +929,8 @@
     unsigned        fTextEncoding : 2;  // 3 values
     unsigned        fHinting : 2;
 #ifdef SK_BUILD_FOR_ANDROID
-    SkString        fTextLocale;
+    SkLanguage      fLanguage;
+    FontVariant     fFontVariant;
 #endif
 
     SkDrawCacheProc    getDrawCacheProc() const;
diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h
index 29679d6..4a0c70d 100644
--- a/include/core/SkScalerContext.h
+++ b/include/core/SkScalerContext.h
@@ -15,6 +15,11 @@
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkPoint.h"
+#include "SkTypeface.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkLanguage.h"
+#endif
 
 //#define SK_USE_COLOR_LUMINANCE
 
@@ -209,6 +214,10 @@
 #ifdef SK_USE_COLOR_LUMINANCE
         uint32_t    fLumBits;
 #endif
+#ifdef SK_BUILD_FOR_ANDROID
+        SkLanguage fLanguage;
+        SkPaint::FontVariant fFontVariant;
+#endif
         uint8_t     fMaskFormat;
         uint8_t     fStrokeJoin;
         uint16_t    fFlags;
@@ -233,7 +242,6 @@
         SkMask::Format getFormat() const {
             return static_cast<SkMask::Format>(fMaskFormat);
         }
-        
 #ifdef SK_USE_COLOR_LUMINANCE
         SkColor getLuminanceColor() const {
             return fLumBits;
@@ -299,6 +307,10 @@
                                SkPaint::FontMetrics* mY);
 
 #ifdef SK_BUILD_FOR_ANDROID
+    // This function must be public for SkTypeface_android.h, but should not be
+    // called by other callers
+    SkFontID findTypefaceIdForChar(SkUnichar uni);
+
     unsigned getBaseGlyphCount(SkUnichar charCode);
 #endif
 
@@ -325,6 +337,8 @@
     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
 
 private:
+    SkScalerContext* getContextFromChar(SkUnichar uni, unsigned& glyphID);
+
     SkPathEffect*   fPathEffect;
     SkMaskFilter*   fMaskFilter;
     SkRasterizer*   fRasterizer;
diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h
index 0023879..c3eb3d1 100644
--- a/include/ports/SkTypeface_android.h
+++ b/include/ports/SkTypeface_android.h
@@ -11,28 +11,19 @@
 
 #include "SkTypeface.h"
 #include "SkPaint.h"
+
 #include "../harfbuzz/src/harfbuzz-shaper.h"
 
-enum FallbackScripts {
-    kArabic_FallbackScript,
-    kArmenian_FallbackScript,
-    kBengali_FallbackScript,
-    kDevanagari_FallbackScript,
-    kEthiopic_FallbackScript,
-    kGeorgian_FallbackScript,
-    kHebrewRegular_FallbackScript,
-    kHebrewBold_FallbackScript,
-    kKannada_FallbackScript,
-    kMalayalam_FallbackScript,
-    kTamilRegular_FallbackScript,
-    kTamilBold_FallbackScript,
-    kThai_FallbackScript,
-    kTelugu_FallbackScript,
-    kFallbackScriptNumber
-};
-
-
+/**
+ *  Return a new typeface for a fallback script. If the script is
+ *  not valid, or can not map to a font, returns null.
+ *  @param  script   The harfbuzz script id.
+ *  @param  style    The font style, for example bold
+ *  @param  elegant  true if we want the web friendly elegant version of the font
+ *  @return          reference to the matching typeface. Caller must call
+ *                   unref() when they are done.
+ */
 SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
-        SkPaint::FontVariant fontVariant) { return NULL; }
+        SkPaint::FontVariant fontVariant = SkPaint::kDefault_Variant);
 
 #endif
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 7861636..0e26728 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -425,7 +425,8 @@
 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
     inc_canvas();
 
-    this->init(NULL);
+    SkBitmap bitmap;
+    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
 }
 
 SkCanvas::SkCanvas(SkDevice* device)
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
index 2895c54..23f3c55 100644
--- a/src/core/SkGlyphCache.h
+++ b/src/core/SkGlyphCache.h
@@ -123,6 +123,8 @@
     //  call the proc)
     void removeAuxProc(void (*auxProc)(void*));
 
+    SkScalerContext* getScalerContext() const { return fScalerContext; }
+
     /** Call proc on all cache entries, stopping early if proc returns true.
         The proc should not create or delete caches, since it could produce
         deadlock.
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index fc5e57c..35b5b38 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -32,6 +32,7 @@
 //#define SK_REPORT_API_RANGE_CHECK
 
 #ifdef SK_BUILD_FOR_ANDROID
+#include "SkLanguage.h"
 #define GEN_ID_INC                  fGenerationID++
 #define GEN_ID_INC_EVAL(expression) if (expression) { fGenerationID++; }
 #else
@@ -71,7 +72,8 @@
     fTextEncoding = kUTF8_TextEncoding;
     fHinting    = SkPaintDefaults_Hinting;
 #ifdef SK_BUILD_FOR_ANDROID
-    new(&fTextLocale) SkString();
+    fLanguage = SkLanguage();
+    fFontVariant = kDefault_Variant;
     fGenerationID = 0;
 #endif
 }
@@ -88,9 +90,6 @@
     SkSafeRef(fRasterizer);
     SkSafeRef(fLooper);
     SkSafeRef(fImageFilter);
-#ifdef SK_BUILD_FOR_ANDROID
-    new(&fTextLocale) SkString(src.fTextLocale);
-#endif
 }
 
 SkPaint::~SkPaint() {
@@ -129,12 +128,10 @@
     SkSafeUnref(fImageFilter);
 
 #ifdef SK_BUILD_FOR_ANDROID
-    fTextLocale.~SkString();
     uint32_t oldGenerationID = fGenerationID;
 #endif
     memcpy(this, &src, sizeof(src));
 #ifdef SK_BUILD_FOR_ANDROID
-    new(&fTextLocale) SkString(src.fTextLocale);
     fGenerationID = oldGenerationID + 1;
 #endif
 
@@ -366,16 +363,24 @@
 }
 
 #ifdef SK_BUILD_FOR_ANDROID
-void SkPaint::setTextLocale(const SkString& locale) {
-    if(!fTextLocale.equals(locale)) {
-        fTextLocale.set(locale);
+void SkPaint::setLanguage(const SkLanguage& language) {
+    if(fLanguage != language) {
+        fLanguage = language;
         GEN_ID_INC;
     }
 }
 
-void SkPaint::setLanguage(const SkLanguage& language) {
-    setTextLocale(SkString(language.getTag()));
+void SkPaint::setFontVariant(FontVariant fontVariant) {
+    if ((unsigned)fontVariant <= kLast_Variant) {
+        GEN_ID_INC_EVAL((unsigned)fontVariant != fFontVariant);
+        fFontVariant = fontVariant;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setFontVariant(%d) out of range\n", fontVariant);
+#endif
+    }
 }
+
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1565,6 +1570,10 @@
 #else
     rec->setLuminanceBits(computeLuminance(paint));
 #endif
+#ifdef SK_BUILD_FOR_ANDROID
+    rec->fLanguage = paint.getLanguage();
+    rec->fFontVariant = paint.getFontVariant();
+#endif //SK_BUILD_FOR_ANDROID
 
     /*  Allow the fonthost to modify our rec before we use it as a key into the
         cache. This way if we're asking for something that they will ignore,
@@ -1840,6 +1849,12 @@
     *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
                     this->getStyle(), this->getTextEncoding());
 
+#ifdef SK_BUILD_FOR_ANDROID
+    buffer.writeInt(this->getFontVariant());
+    const SkString& langTag = this->getLanguage().getTag();
+    buffer.writeString(langTag.c_str(), langTag.size());
+#endif
+
     // now we're done with ptr and the (pre)reserved space. If we need to write
     // additional fields, use the buffer directly
     if (flatFlags & kHasTypeface_FlatFlag) {
@@ -1894,6 +1909,11 @@
     this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
     this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
 
+#ifdef SK_BUILD_FOR_ANDROID
+    this->setFontVariant(SkPaint::FontVariant(buffer.readInt()));
+    this->setLanguage(SkLanguage(buffer.readString()));
+#endif
+
     if (flatFlags & kHasTypeface_FlatFlag) {
         this->setTypeface(buffer.readTypeface());
     } else {
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index 2921b1e..eee0dc5 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -118,7 +118,13 @@
     // fonthost will determine the next possible font to search, based
     // on the current font in fRec. It will return NULL if ctx is our
     // last font that can be searched (i.e. ultimate fallback font)
-    uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID);
+#ifdef SK_BUILD_FOR_ANDROID
+        // On Android, pass entire rec structure so that clients can change fallback behavior
+        uint32_t newFontID = SkFontHost::NextLogicalFont(rec);
+#else
+        uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID);
+#endif
+
     if (0 == newFontID) {
         return NULL;
     }
@@ -156,6 +162,21 @@
     return next;
 }
 
+SkScalerContext* SkScalerContext::getContextFromChar(SkUnichar uni, unsigned& glyphID) {
+    SkScalerContext* ctx = this;
+    for (;;) {
+        glyphID = ctx->generateCharToGlyph(uni);
+        if (glyphID) {
+            break;  // found it
+        }
+        ctx = ctx->getNextContext();
+        if (NULL == ctx) {
+            return NULL;
+        }
+    }
+    return ctx;
+}
+
 SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
     unsigned glyphID = glyph.getGlyphID();
     SkScalerContext* ctx = this;
@@ -176,6 +197,16 @@
 }
 
 #ifdef SK_BUILD_FOR_ANDROID
+SkFontID SkScalerContext::findTypefaceIdForChar(SkUnichar uni) {
+    unsigned glyphID;
+    SkScalerContext* ctx = getContextFromChar(uni, glyphID);
+    if (ctx) {
+        return ctx->fRec.fFontID;
+    } else {
+        return 0;
+    }
+}
+
 /*  This loops through all available fallback contexts (if needed) until it
     finds some context that can handle the unichar and return it.
 
@@ -183,21 +214,14 @@
     char of a run.
  */
 unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) {
-    SkScalerContext* ctx = this;
     unsigned glyphID;
-    for (;;) {
-        glyphID = ctx->generateCharToGlyph(uni);
-        if (glyphID) {
-            break;  // found it
-        }
-        ctx = ctx->getNextContext();
-        if (NULL == ctx) {
-            SkDebugf("--- no context for char %x\n", uni);
-            // just return the original context (this)
-            return this->fBaseGlyphCount;
-        }
+    SkScalerContext* ctx = getContextFromChar(uni, glyphID);
+    if (ctx) {
+        return ctx->fBaseGlyphCount;
+    } else {
+        SkDEBUGF(("--- no context for char %x\n", uni));
+        return this->fBaseGlyphCount;
     }
-    return ctx->fBaseGlyphCount;
 }
 #endif
 
@@ -205,17 +229,11 @@
     finds some context that can handle the unichar. If all fail, returns 0
  */
 uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
-    SkScalerContext* ctx = this;
+
     unsigned glyphID;
-    for (;;) {
-        glyphID = ctx->generateCharToGlyph(uni);
-        if (glyphID) {
-            break;  // found it
-        }
-        ctx = ctx->getNextContext();
-        if (NULL == ctx) {
-            return 0;   // no more contexts, return missing glyph
-        }
+    SkScalerContext* ctx = getContextFromChar(uni, glyphID);
+    if (!ctx) {
+        return 0; // no more contexts, return missing glyph
     }
     // add the ctx's base, making glyphID unique for chain of contexts
     glyphID += ctx->fBaseGlyphCount;
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index 3e416cc..3ca1254 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -34,7 +34,6 @@
 // If moving libwebp out of skia source tree, path for webp headers must be
 // updated accordingly. Here, we enforce using local copy in webp sub-directory.
 #include "webp/decode.h"
-#include "webp/decode_vp8.h"
 #include "webp/encode.h"
 }
 
@@ -56,20 +55,29 @@
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 
-static const size_t WEBP_VP8_HEADER_SIZE = 30;
+static const size_t WEBP_VP8_HEADER_SIZE = 64;
 static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
 
 // Parse headers of RIFF container, and check for valid Webp (VP8) content.
-static bool webp_parse_header(SkStream* stream, int* width, int* height) {
+static bool webp_parse_header(SkStream* stream, int* width, int* height,
+                              int* alpha) {
     unsigned char buffer[WEBP_VP8_HEADER_SIZE];
+    const uint32_t contentSize = stream->getLength();
     const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE);
-    if (len != WEBP_VP8_HEADER_SIZE) {
+    const uint32_t read_bytes = (contentSize < WEBP_VP8_HEADER_SIZE) ?
+        contentSize : WEBP_VP8_HEADER_SIZE;
+    if (len != read_bytes) {
         return false; // can't read enough
     }
 
-    if (WebPGetInfo(buffer, WEBP_VP8_HEADER_SIZE, width, height) == 0) {
+    WebPBitstreamFeatures features;
+    VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features);
+    if (status != VP8_STATUS_OK) {
         return false; // Invalid WebP file.
     }
+    *width = features.width;
+    *height = features.height;
+    *alpha = features.has_alpha;
 
     // sanity check for image size that's about to be decoded.
     {
@@ -102,6 +110,7 @@
     SkStream *inputStream;
     int origWidth;
     int origHeight;
+    int hasAlpha;
 };
 
 //////////////////////////////////////////////////////////////////////////
@@ -136,16 +145,20 @@
     return false; // must always return false
 }
 
-static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap) {
+static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap, int hasAlpha) {
     WEBP_CSP_MODE mode = MODE_LAST;
     SkBitmap::Config config = decodedBitmap->config();
+    // For images that have alpha, choose appropriate color mode (MODE_rgbA,
+    // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency
+    // factor (alpha).
     if (config == SkBitmap::kARGB_8888_Config) {
-      mode = MODE_RGBA;
+      mode = hasAlpha ? MODE_rgbA : MODE_RGBA;
     } else if (config == SkBitmap::kARGB_4444_Config) {
-      mode = MODE_RGBA_4444;
+      mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444;
     } else if (config == SkBitmap::kRGB_565_Config) {
       mode = MODE_RGB_565;
     }
+    SkASSERT(mode != MODE_LAST);
     return mode;
 }
 
@@ -160,10 +173,8 @@
 
     stream->rewind();
     const uint32_t contentSize = stream->getLength();
-    uint32_t read_buffer_size = contentSize;
-    if (read_buffer_size > WEBP_IDECODE_BUFFER_SZ) {
-        read_buffer_size = WEBP_IDECODE_BUFFER_SZ;
-    }
+    const uint32_t read_buffer_size = (contentSize < WEBP_IDECODE_BUFFER_SZ) ?
+        contentSize : WEBP_IDECODE_BUFFER_SZ;
     SkAutoMalloc srcStorage(read_buffer_size);
     unsigned char* input = (uint8_t*)srcStorage.get();
     if (input == NULL) {
@@ -175,8 +186,8 @@
     uint32_t bytes_remaining = contentSize;
     while (bytes_remaining > 0) {
         const uint32_t bytes_to_read =
-            (bytes_remaining > WEBP_IDECODE_BUFFER_SZ) ?
-                WEBP_IDECODE_BUFFER_SZ : bytes_remaining;
+            (bytes_remaining < WEBP_IDECODE_BUFFER_SZ) ?
+                bytes_remaining : WEBP_IDECODE_BUFFER_SZ;
 
         const size_t bytes_read = stream->read(input, bytes_to_read);
         if (bytes_read == 0) {
@@ -201,10 +212,10 @@
     }
 }
 
-static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
-                                        SkBitmap* decodedBitmap,
-                                        SkIRect region) {
-    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
+static bool webp_get_config_resize(WebPDecoderConfig& config,
+                                   SkBitmap* decodedBitmap,
+                                   int width, int height, int hasAlpha) {
+    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha);
     if (mode == MODE_LAST) {
         return false;
     }
@@ -219,53 +230,36 @@
     config.output.u.RGBA.size = decodedBitmap->getSize();
     config.output.is_external_memory = 1;
 
+    if (width != decodedBitmap->width() ||
+        height != decodedBitmap->height()) {
+        config.options.use_scaling = 1;
+        config.options.scaled_width = decodedBitmap->width();
+        config.options.scaled_height = decodedBitmap->height();
+    }
+
+    return true;
+}
+
+static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
+                                        SkBitmap* decodedBitmap,
+                                        SkIRect region, int hasAlpha) {
+
+    if (!webp_get_config_resize(
+        config, decodedBitmap, region.width(), region.height(), hasAlpha)) {
+      return false;
+    }
+
     config.options.use_cropping = 1;
     config.options.crop_left = region.fLeft;
     config.options.crop_top = region.fTop;
     config.options.crop_width = region.width();
     config.options.crop_height = region.height();
 
-    if (region.width() != decodedBitmap->width() ||
-        region.height() != decodedBitmap->height()) {
-        config.options.use_scaling = 1;
-        config.options.scaled_width = decodedBitmap->width();
-        config.options.scaled_height = decodedBitmap->height();
-    }
-
-    return true;
-}
-
-static bool webp_get_config_resize(WebPDecoderConfig& config,
-                                   SkBitmap* decodedBitmap, int origWidth,
-                                   int origHeight) {
-    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
-    if (mode == MODE_LAST) {
-        return false;
-    }
-
-    if (WebPInitDecoderConfig(&config) == 0) {
-        return false;
-    }
-
-    config.output.colorspace = mode;
-    config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
-    config.output.u.RGBA.stride = decodedBitmap->rowBytes();
-    config.output.u.RGBA.size = decodedBitmap->getSize();
-    config.output.is_external_memory = 1;
-
-    if (origWidth != decodedBitmap->width() ||
-        origHeight != decodedBitmap->height()) {
-        config.options.use_scaling = 1;
-        config.options.scaled_width = decodedBitmap->width();
-        config.options.scaled_height = decodedBitmap->height();
-    }
-
     return true;
 }
 
 bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
                                          int width, int height) {
-    bool hasAlpha = false;
     SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
 
     // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
@@ -286,16 +280,15 @@
 
     decodedBitmap->setConfig(config, width, height, 0);
 
-    // Current WEBP specification has no support for alpha layer.
-    decodedBitmap->setIsOpaque(true);
+    decodedBitmap->setIsOpaque(!hasAlpha);
 
     return true;
 }
 
 bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
                                           int *width, int *height) {
-    int origWidth, origHeight;
-    if (!webp_parse_header(stream, &origWidth, &origHeight)) {
+    int origWidth, origHeight, hasAlpha;
+    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
         return false;
     }
 
@@ -306,11 +299,12 @@
     this->inputStream = stream;
     this->origWidth = origWidth;
     this->origHeight = origHeight;
+    this->hasAlpha = hasAlpha;
 
     return true;
 }
 
-static bool isConfigCompatiable(SkBitmap* bitmap) {
+static bool isConfigCompatible(SkBitmap* bitmap) {
     SkBitmap::Config config = bitmap->config();
     return config == SkBitmap::kARGB_4444_Config ||
            config == SkBitmap::kRGB_565_Config ||
@@ -338,9 +332,9 @@
     //   3. bitmap's size is same as the required region (after sampled)
     bool directDecode = (rect == region) &&
                         (decodedBitmap->isNull() ||
-                         isConfigCompatiable(decodedBitmap) &&
+                         (isConfigCompatible(decodedBitmap) &&
                          (decodedBitmap->width() == width) &&
-                         (decodedBitmap->height() == height));
+                         (decodedBitmap->height() == height)));
     SkTScopedPtr<SkBitmap> adb;
     SkBitmap *bitmap = decodedBitmap;
 
@@ -371,7 +365,7 @@
 
     SkAutoLockPixels alp(*bitmap);
     WebPDecoderConfig config;
-    if (!webp_get_config_resize_crop(config, bitmap, rect)) {
+    if (!webp_get_config_resize_crop(config, bitmap, rect, hasAlpha)) {
         return false;
     }
 
@@ -394,10 +388,11 @@
     AutoTimeMillis atm("WEBP Decode");
 #endif
 
-    int origWidth, origHeight;
-    if (!webp_parse_header(stream, &origWidth, &origHeight)) {
+    int origWidth, origHeight, hasAlpha;
+    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
         return false;
     }
+    this->hasAlpha = hasAlpha;
 
     const int sampleSize = this->getSampleSize();
     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
@@ -428,7 +423,8 @@
     SkAutoLockPixels alp(*decodedBitmap);
 
     WebPDecoderConfig config;
-    if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight)) {
+    if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight,
+                                hasAlpha)) {
         return false;
     }
 
@@ -568,9 +564,9 @@
 #include "SkTRegistry.h"
 
 static SkImageDecoder* DFactory(SkStream* stream) {
-    int width, height;
-    if (!webp_parse_header(stream, &width, &height)) {
-        return false;
+    int width, height, hasAlpha;
+    if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
+        return NULL;
     }
 
     // Magic matches, call decoder
diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp
index d1164c8..420ad1c 100644
--- a/src/ports/FontHostConfiguration_android.cpp
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -16,8 +16,9 @@
 */
 
 #include "FontHostConfiguration_android.h"
-#include "SkString.h"
+#include "SkLanguage.h"
 #include "SkTDArray.h"
+#include "SkTypeface.h"
 #include <expat.h>
 #if !defined(SK_BUILD_FOR_ANDROID_NDK)
     #include <cutils/properties.h>
@@ -27,7 +28,6 @@
 #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml"
 #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml"
 
-
 // These defines are used to determine the kind of tag that we're currently
 // populating with data. We only care about the sibling tags nameset and fileset
 // for now.
@@ -46,12 +46,13 @@
     XML_Parser *parser;                // The expat parser doing the work
     SkTDArray<FontFamily*> &families;  // The array that each family is put into as it is parsed
     FontFamily *currentFamily;         // The current family being created
+    FontFileInfo *currentFontInfo;     // The current fontInfo being created
     int currentTag;                    // A flag to indicate whether we're in nameset/fileset tags
 };
 
 /**
  * Handler for arbitrary text. This is used to parse the text inside each name
- * or file tag. The resulting strings are put into the fNames or fFileNames arrays.
+ * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays.
  */
 void textHandler(void *data, const char *s, int len) {
     FamilyData *familyData = (FamilyData*) data;
@@ -68,7 +69,9 @@
             *(familyData->currentFamily->fNames.append()) = buff;
             break;
         case FILESET_TAG:
-            *(familyData->currentFamily->fFileNames.append()) = buff;
+            if (familyData->currentFontInfo) {
+                familyData->currentFontInfo->fFileName = buff;
+            }
             break;
         default:
             // Noop - don't care about any text that's not in the Fonts or Names list
@@ -78,6 +81,37 @@
 }
 
 /**
+ * Handler for font files. This processes the attributes for language and
+ * variants then lets textHandler handle the actual file name
+ */
+void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
+    FontFileInfo* newFileInfo = new FontFileInfo();
+    if (attributes) {
+        int currentAttributeIndex = 0;
+        while (attributes[currentAttributeIndex]) {
+            const char* attributeName = attributes[currentAttributeIndex];
+            const char* attributeValue = attributes[currentAttributeIndex+1];
+            int nameLength = strlen(attributeName);
+            int valueLength = strlen(attributeValue);
+            if (strncmp(attributeName, "variant", nameLength) == 0) {
+                if (strncmp(attributeValue, "elegant", valueLength) == 0) {
+                    newFileInfo->fVariant = SkPaint::kElegant_Variant;
+                } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
+                    newFileInfo->fVariant = SkPaint::kCompact_Variant;
+                }
+            } else if (strncmp(attributeName, "lang", nameLength) == 0) {
+                newFileInfo->fLanguage = SkLanguage(attributeValue);
+            }
+            //each element is a pair of attributeName/attributeValue string pairs
+            currentAttributeIndex += 2;
+        }
+    }
+    *(familyData->currentFamily->fFontFileArray.append()) = newFileInfo;
+    familyData->currentFontInfo = newFileInfo;
+    XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+}
+
+/**
  * Handler for the start of a tag. The only tags we expect are family, nameset,
  * fileset, name, and file.
  */
@@ -98,14 +132,16 @@
                 familyData->currentFamily->order = value;
             }
         }
-    } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
+    } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
         familyData->currentTag = NAMESET_TAG;
     } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
         familyData->currentTag = FILESET_TAG;
-    } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
-            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+    } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) {
         // If it's a Name, parse the text inside
         XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+    } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) {
+        // If it's a file, parse the attributes, then parse the text inside
+        fontFileElementHandler(familyData, atts);
     }
 }
 
@@ -120,9 +156,9 @@
         // Done parsing a Family - store the created currentFamily in the families array
         *familyData->families.append() = familyData->currentFamily;
         familyData->currentFamily = NULL;
-    } else if (len == 7 && strncmp(tag, "nameset", len)== 0) {
+    } else if (len == 7 && strncmp(tag, "nameset", len) == 0) {
         familyData->currentTag = NO_TAG;
-    } else if (len == 7 && strncmp(tag, "fileset", len)== 0) {
+    } else if (len == 7 && strncmp(tag, "fileset", len) == 0) {
         familyData->currentTag = NO_TAG;
     } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) ||
             (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
@@ -131,65 +167,6 @@
     }
 }
 
-#if !defined(SK_BUILD_FOR_ANDROID_NDK)
-/**
- * Read the persistent locale.
- */
-void getLocale(char* language, char* region)
-{
-    char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX];
-
-    property_get("persist.sys.language", propLang, "");
-    property_get("persist.sys.country", propRegn, "");
-    if (*propLang == 0 && *propRegn == 0) {
-        /* Set to ro properties, default is en_US */
-        property_get("ro.product.locale.language", propLang, "en");
-        property_get("ro.product.locale.region", propRegn, "US");
-    }
-    strncat(language, propLang, 2);
-    strncat(region, propRegn, 2);
-}
-#endif
-
-/**
- * Use the current system locale (language and region) to open the best matching
- * customization. For example, when the language is Japanese, the sequence might be:
- *      /system/etc/fallback_fonts-ja-JP.xml
- *      /system/etc/fallback_fonts-ja.xml
- *      /system/etc/fallback_fonts.xml
- */
-FILE* openLocalizedFile(const char* origname) {
-    FILE* file = 0;
-
-#if !defined(SK_BUILD_FOR_ANDROID_NDK)
-    SkString basename;
-    SkString filename;
-    char language[3] = "";
-    char region[3] = "";
-
-    basename.set(origname);
-    // Remove the .xml suffix. We'll add it back in a moment.
-    if (basename.endsWith(".xml")) {
-        basename.resize(basename.size()-4);
-    }
-    getLocale(language, region);
-    // Try first with language and region
-    filename.printf("%s-%s-%s.xml", basename.c_str(), language, region);
-    file = fopen(filename.c_str(), "r");
-    if (!file) {
-        // If not found, try next with just language
-        filename.printf("%s-%s.xml", basename.c_str(), language);
-        file = fopen(filename.c_str(), "r");
-    }
-#endif
-
-    if (!file) {
-        // If still not found, try just the original name
-        file = fopen(origname, "r");
-    }
-    return file;
-}
-
 /**
  * This function parses the given filename and stores the results in the given
  * families array.
@@ -199,7 +176,7 @@
     FamilyData *familyData = new FamilyData(&parser, families);
     XML_SetUserData(parser, familyData);
     XML_SetElementHandler(parser, startElementHandler, endElementHandler);
-    FILE *file = openLocalizedFile(filename);
+    FILE *file = fopen(filename, "r");
     // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml)
     // are optional - failure here is okay because one of these optional files may not exist.
     if (file == NULL) {
@@ -215,6 +192,7 @@
         }
         XML_Parse(parser, buffer, len, done);
     }
+    fclose(file);
 }
 
 void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) {
diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h
index 2441f0e..6734b08 100644
--- a/src/ports/FontHostConfiguration_android.h
+++ b/src/ports/FontHostConfiguration_android.h
@@ -17,19 +17,33 @@
 #ifndef FONTHOSTCONFIGURATION_ANDROID_H_
 #define FONTHOSTCONFIGURATION_ANDROID_H_
 
+#include "SkTypes.h"
+
+#include "SkLanguage.h"
+#include "SkPaint.h"
 #include "SkTDArray.h"
 
+struct FontFileInfo {
+    FontFileInfo() : fFileName(NULL), fVariant(SkPaint::kDefault_Variant),
+            fLanguage() {
+    }
+
+    const char*          fFileName;
+    SkPaint::FontVariant fVariant;
+    SkLanguage           fLanguage;
+};
+
 /**
  * The FontFamily data structure is created during parsing and handed back to
  * Skia to fold into its representation of font families. fNames is the list of
- * font names that alias to a font family. fFileNames is the list of font
- * filenames for the family. Order is the priority order for the font. This is
+ * font names that alias to a font family. fontFileArray is the list of information
+ * about each file.  Order is the priority order for the font. This is
  * used internally to determine the order in which to place fallback fonts as
  * they are read from the configuration files.
  */
 struct FontFamily {
-    SkTDArray<const char*>  fNames;
-    SkTDArray<const char*>  fFileNames;
+    SkTDArray<const char*>   fNames;
+    SkTDArray<FontFileInfo*> fFontFileArray;
     int order;
 };
 
@@ -45,15 +59,10 @@
  */
 void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
 
-
 /**
  * Parse the fallback and vendor system font configuration files and return the
  * results in an array of FontFamily structures.
  */
 void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts);
 
-#if !defined(SK_BUILD_FOR_ANDROID_NDK)
-    void getLocale(char* language, char* region);
-#endif
-
 #endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index fc60ef9..d77ebb9 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -695,6 +695,13 @@
 #ifdef SK_BUILD_FOR_ANDROID
 uint32_t SkFontHost::GetUnitsPerEm(SkFontID fontID) {
     SkAutoMutexAcquire ac(gFTMutex);
+    FT_Library libInit = NULL;
+    if (gFTCount == 0) {
+        if (!InitFreetype())
+            sk_throw();
+        libInit = gFTLibrary;
+    }
+    SkAutoTCallIProc<struct FT_LibraryRec_, FT_Done_FreeType> ftLib(libInit);
     SkFaceRec *rec = ref_ft_face(fontID);
     uint16_t unitsPerEm = 0;
 
@@ -961,8 +968,8 @@
         if (0 == error) {
             glyph->fRsbDelta = 0;
             glyph->fLsbDelta = 0;
-            glyph->fAdvanceX = advance;  // advance *2/3; //DEBUG
-            glyph->fAdvanceY = 0;
+            glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance);  // advance *2/3; //DEBUG
+            glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, advance);
             return;
         }
     }
diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
index 2c58079..dddadd0 100644
--- a/src/ports/SkFontHost_android.cpp
+++ b/src/ports/SkFontHost_android.cpp
@@ -27,6 +27,12 @@
 #include "FontHostConfiguration_android.h"
 #include <stdio.h>
 #include <string.h>
+#include "SkGlyphCache.h"
+#include "SkLanguage.h"
+#include "SkTypeface_android.h"
+#include "SkTArray.h"
+#include "SkTDict.h"
+#include "SkTSearch.h"
 
 //#define SkDEBUGF(args       )       SkDebugf args
 
@@ -73,9 +79,10 @@
         SkTypeface::Style style);
 static SkStream* openStreamLocked(uint32_t fontID);
 static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int32_t* index);
-static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID);
+static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec);
 static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream);
 
+
 ///////////////////////////////////////////////////////////////////////////////
 
 struct FamilyRec;
@@ -147,6 +154,12 @@
     return NULL;
 }
 
+static SkTypeface* FindBestFace(const FamilyRec* family,
+            SkTypeface::Style style) {
+    SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
+    return findBestFaceLocked(family, style);
+}
+
 static FamilyRec* findFamilyLocked(const SkTypeface* member) {
     FamilyRec* curr = gFamilyHead;
     while (curr != NULL) {
@@ -177,6 +190,14 @@
     return NULL;
 }
 
+/*  Returns the matching typeface, or NULL. If a typeface is found, its refcnt
+    is not modified.
+ */
+static SkTypeface* FindFromUniqueID(uint32_t uniqueID) {
+    SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
+    return findFromUniqueIDLocked(uniqueID);
+}
+
 /*  Remove reference to this face from its family. If the resulting family
     is empty (has no faces), return that family, otherwise return NULL
 */
@@ -394,26 +415,40 @@
 
 // used to record our notion of the pre-existing fonts
 struct FontInitRec {
-    const char*         fFileName;
-    const char* const*  fNames;     // null-terminated list
+    const char*          fFileName;
+    const char* const*   fNames;     // null-terminated list
+    SkPaint::FontVariant fVariant;
+    SkLanguage           fLanguage;
+};
+
+//used to record information about the fallback fonts
+struct FallbackFontRec {
+    SkFontID             fFontID;
+    SkPaint::FontVariant fVariant;
+};
+
+struct FallbackFontList {
+    FallbackFontList(const SkLanguage& language) : fLanguage(language) { }
+    SkTDArray<FallbackFontRec> fList;
+    SkLanguage                 fLanguage;
 };
 
 // deliberately empty, but we use the address to identify fallback fonts
 static const char* gFBNames[] = { NULL };
 
-
 /*  Fonts are grouped by family, with the first font in a family having the
     list of names (even if that list is empty), and the following members having
     null for the list. The names list must be NULL-terminated.
 */
-static SkTDArray<FontInitRec> gSystemFonts;
-static SkTDArray<SkFontID> gFallbackFonts;
+static SkTArray<FontInitRec> gSystemFonts;
+static SkTDArray<FallbackFontList*> gFallbackFontLists;
 
 // these globals are assigned (once) by loadSystemFontsLocked()
 static FamilyRec* gDefaultFamily = NULL;
 static SkTypeface* gDefaultNormal = NULL;
 static char** gDefaultNames = NULL;
 
+static FallbackFontList* getFallbackFontListLocked(const SkLanguage& lang);
 static void dumpGlobalsLocked() {
     SkDebugf("gDefaultNormal=%p id=%u refCnt=%d", gDefaultNormal,
              gDefaultNormal ? gDefaultNormal->uniqueID() : 0,
@@ -434,8 +469,11 @@
         SkDebugf("gDefaultFamily=%p", gDefaultFamily);
     }
 
-    SkDebugf("gSystemFonts.count()=%d gFallbackFonts.count()=%d",
-            gSystemFonts.count(), gFallbackFonts.count());
+    FallbackFontList* defaultFallbackList =
+            getFallbackFontListLocked(SkLanguage());
+    SkASSERT(defaultFallbackList != NULL);
+    SkDebugf("gSystemFonts.count()=%d defaultFallbackList->fList.count()=%d",
+           gSystemFonts.count(), defaultFallbackList->fList.count());
 
     for (int i = 0; i < gSystemFonts.count(); ++i) {
         SkDebugf("gSystemFonts[%d] fileName=%s", i, gSystemFonts[i].fFileName);
@@ -480,9 +518,164 @@
     return false;
 }
 
+// (SkLanguage)<->(fallback chain index) translation
+static const size_t kLangDictSize = 128;
+static SkTDict<FallbackFontList*> gLangTagToFallbackFontList(kLangDictSize);
+static bool gIsOKToUseFallbackFontListCache = false;
+
+// crawl fallback font lists by hand looking for a specific language
+static FallbackFontList* getFallbackFontListNoCacheLocked(
+        const SkLanguage& lang) {
+    unsigned int numLists = gFallbackFontLists.count();
+    for (unsigned int listIdx = 0; listIdx < numLists; ++listIdx) {
+        FallbackFontList* list = gFallbackFontLists[listIdx];
+        SkASSERT(list != NULL);
+        if (list->fLanguage == lang) {
+            return list;
+        }
+    }
+    return NULL;
+}
+
+// perform fancy fuzzy-matching memoized query for a fallback font list.
+// should only be called after fallback font lists are fully loaded.
+static FallbackFontList* getFallbackFontListLocked(const SkLanguage& lang) {
+    SkASSERT(gIsOKToUseFallbackFontListCache);
+    const SkString& langTag = lang.getTag();
+    FallbackFontList* fallbackFontList;
+    if (gLangTagToFallbackFontList.find(langTag.c_str(), langTag.size(),
+            &fallbackFontList)) {
+        // cache hit!
+        return fallbackFontList;
+    }
+
+    // try again without the cache
+    fallbackFontList = getFallbackFontListNoCacheLocked(lang);
+    if (fallbackFontList != NULL) {
+        // found it - cache and return
+        gLangTagToFallbackFontList.set(langTag.c_str(), langTag.size(),
+                fallbackFontList);
+        SkDEBUGF(("new fallback cache entry: \"%s\"", langTag.c_str()));
+        return fallbackFontList;
+    }
+
+    // no hit - can we fuzzy-match?
+    if (lang.getTag().isEmpty()) {
+        // nope! this happens if attempting to direct match with no default
+        return NULL;
+    }
+
+    // attempt fuzzy match
+    SkLanguage parent = lang.getParent();
+    fallbackFontList = getFallbackFontListLocked(parent);
+    if (fallbackFontList != NULL) {
+        // found it - cache and return
+        gLangTagToFallbackFontList.set(langTag.c_str(), langTag.size(),
+                fallbackFontList);
+        SkDEBUGF(("new fallback cache entry: \"%s\" -> \"%s\"", langTag.c_str(),
+                fallbackFontList->fLanguage.getTag().c_str()));
+        return fallbackFontList;
+    }
+
+    // utter failure. this happens if attempting to fuzzy-match with no default
+    SkASSERT(fallbackFontList != NULL);
+    return NULL;
+}
+
+// creates a new fallback font list for the specified language
+static FallbackFontList* createFallbackFontListLocked(const SkLanguage& lang) {
+    SkASSERT(!gIsOKToUseFallbackFontListCache);
+    SkDEBUGF(("new fallback list: \"%s\"", lang.getTag().c_str()));
+    FallbackFontList* fallbackFontList = new FallbackFontList(lang);
+    gFallbackFontLists.push(fallbackFontList);
+    return fallbackFontList;
+}
+
+// adds a fallback font record to both the default fallback chain and the
+// language-specific fallback chain to which it belongs, if any
+static void addFallbackFontLocked(const FallbackFontRec& fallbackRec,
+        const SkLanguage& lang) {
+    SkASSERT(!gIsOKToUseFallbackFontListCache);
+    SkDEBUGF(("new fallback font: %d, in \"%s\"", fallbackRec.fFontID,
+            lang.getTag().c_str()));
+    // add to the default fallback list
+    FallbackFontList* fallbackList =
+            getFallbackFontListNoCacheLocked(SkLanguage());
+    if (fallbackList == NULL) {
+        // oops! no default list yet. create one.
+        fallbackList = createFallbackFontListLocked(SkLanguage());
+    }
+    SkASSERT(fallbackList != NULL);
+    fallbackList->fList.push(fallbackRec);
+    if (lang.getTag().isEmpty()) {
+        return;
+    }
+    // also add to the appropriate language's fallback list
+    fallbackList = getFallbackFontListNoCacheLocked(lang);
+    if (fallbackList == NULL) {
+        // first entry for this list!
+        fallbackList = createFallbackFontListLocked(lang);
+    }
+    SkASSERT(fallbackList != NULL);
+    fallbackList->fList.push(fallbackRec);
+}
+
+static int getSystemFontIndexForFontID(SkFontID fontID) {
+    // font unique id = one-based index in system font table
+    SkASSERT(fontID - 1 < gSystemFonts.count());
+    return fontID - 1;
+}
+
+// scans the default fallback font chain, adding every entry to every other
+// fallback font chain to which it does not belong. this results in every
+// language-specific fallback font chain having all of its fallback fonts at
+// the front of the chain, and everything else at the end. after this has been
+// run, it is ok to use the fallback font chain lookup table.
+static void finaliseFallbackFontListsLocked() {
+    SkASSERT(!gIsOKToUseFallbackFontListCache);
+    // if we have more than one list, we need to finalise non-default lists
+    unsigned int numLists = gFallbackFontLists.count();
+    if (numLists > 1) {
+        // pull fonts off of the default list...
+        FallbackFontList* defaultList = getFallbackFontListNoCacheLocked(
+                SkLanguage());
+        SkASSERT(defaultList != NULL);
+        int numDefaultFonts = defaultList->fList.count();
+        for (int fontIdx = 0; fontIdx < numDefaultFonts; ++fontIdx) {
+            // figure out which language they represent
+            SkFontID fontID = defaultList->fList[fontIdx].fFontID;
+            int sysFontIdx = getSystemFontIndexForFontID(fontID);
+            const SkLanguage& lang = gSystemFonts[sysFontIdx].fLanguage;
+            for (unsigned int listIdx = 0; listIdx < numLists; ++listIdx) {
+                // and add them to every other language's list
+                FallbackFontList* thisList = gFallbackFontLists[listIdx];
+                SkASSERT(thisList != NULL);
+                if (thisList != defaultList && thisList->fLanguage != lang) {
+                    thisList->fList.push(defaultList->fList[fontIdx]);
+                }
+            }
+        }
+    }
+    gIsOKToUseFallbackFontListCache = true;
+}
+
+static void resetFallbackFontListsLocked() {
+    // clear cache
+    gLangTagToFallbackFontList.reset();
+    // clear the data it pointed at
+    int numFallbackLists = gFallbackFontLists.count();
+    for (int fallbackIdx = 0; fallbackIdx < numFallbackLists; ++fallbackIdx) {
+        delete gFallbackFontLists[fallbackIdx];
+    }
+    gFallbackFontLists.reset();
+    gIsOKToUseFallbackFontListCache = false;
+}
+
 /*  Load info from a configuration file that populates the system/fallback font structures
 */
 static void loadFontInfoLocked() {
+    resetFallbackFontListsLocked();
+
     SkTDArray<FontFamily*> fontFamilies;
     getFontFamilies(fontFamilies);
 
@@ -490,8 +683,8 @@
 
     for (int i = 0; i < fontFamilies.count(); ++i) {
         FontFamily *family = fontFamilies[i];
-        for (int j = 0; j < family->fFileNames.count(); ++j) {
-            const char* filename = family->fFileNames[j];
+        for (int j = 0; j < family->fFontFileArray.count(); ++j) {
+            const char* filename = family->fFontFileArray[j]->fFileName;
             if (haveSystemFont(filename)) {
                 SkDebugf("---- system font and fallback font files specify a duplicate "
                         "font %s, skipping the second occurrence", filename);
@@ -500,6 +693,8 @@
 
             FontInitRec fontInfoRecord;
             fontInfoRecord.fFileName = filename;
+            fontInfoRecord.fVariant = family->fFontFileArray[j]->fVariant;
+            fontInfoRecord.fLanguage = family->fFontFileArray[j]->fLanguage;
             if (j == 0) {
                 if (family->fNames.count() == 0) {
                     // Fallback font
@@ -524,7 +719,7 @@
             } else {
                 fontInfoRecord.fNames = NULL;
             }
-            *gSystemFonts.append() = fontInfoRecord;
+            gSystemFonts.push_back(fontInfoRecord);
         }
     }
     fontFamilies.deleteAll();
@@ -535,7 +730,6 @@
     }
 }
 
-
 /*
  *  Called once (ensured by the sentinel check at the beginning of our body).
  *  Initializes all the globals, and register the system fonts.
@@ -550,8 +744,6 @@
 
     loadFontInfoLocked();
 
-    gFallbackFonts.reset();
-
     SkTypeface* firstInFamily = NULL;
     for (int i = 0; i < gSystemFonts.count(); i++) {
         // if we're the first in a new family, clear firstInFamily
@@ -590,9 +782,11 @@
         if (names != NULL) {
             // see if this is one of our fallback fonts
             if (names == gFBNames) {
-                SkDEBUGF(("---- adding %s as fallback[%d] fontID %d\n",
-                        gSystemFonts[i].fFileName, gFallbackFonts.count(), tf->uniqueID()));
-                *gFallbackFonts.append() = tf->uniqueID();
+                // add to appropriate fallback chains
+                FallbackFontRec fallbackRec;
+                fallbackRec.fFontID = tf->uniqueID();
+                fallbackRec.fVariant = gSystemFonts[i].fVariant;
+                addFallbackFontLocked(fallbackRec, gSystemFonts[i].fLanguage);
             }
 
             firstInFamily = tf;
@@ -609,6 +803,7 @@
             }
         }
     }
+    finaliseFallbackFontListsLocked();
 
     // do this after all fonts are loaded. This is our default font, and it
     // acts as a sentinel so we only execute loadSystemFontsLocked() once
@@ -617,100 +812,19 @@
     SkDEBUGCODE(dumpGlobalsLocked());
 }
 
-static SkFontID findUniqueIDLocked(const char* filename) {
-    // uniqueID is the index, offset by one, of the associated element in
-    // gSystemFonts[] (assumes system fonts are loaded before external fonts)
-    // return 0 if not found
-    for (int i = 0; i < gSystemFonts.count(); i++) {
-        if (strcmp(gSystemFonts[i].fFileName, filename) == 0) {
-            return i + 1; // assume unique id of i'th system font is i + 1
-        }
-    }
-    return 0;
-}
-
-static int findFallbackFontIndex(SkFontID fontId) {
-    for (int i = 0; i < gFallbackFonts.count(); i++) {
-        if (gFallbackFonts[i] == fontId) {
+static int findFallbackFontIndex(SkFontID fontId, FallbackFontList* currentFallbackList) {
+    for (int i = 0; i < currentFallbackList->fList.count(); i++) {
+        if (currentFallbackList->fList[i].fFontID == fontId) {
             return i;
         }
     }
     return -1;
 }
 
-static void reloadFallbackFontsLocked() {
-    SkGraphics::PurgeFontCache();
-
-    SkTDArray<FontFamily*> fallbackFamilies;
-    getFallbackFontFamilies(fallbackFamilies);
-
-    gFallbackFonts.reset();
-
-    for (int i = 0; i < fallbackFamilies.count(); ++i) {
-        FontFamily *family = fallbackFamilies[i];
-
-        for (int j = 0; j < family->fFileNames.count(); ++j) {
-            const char* filename = family->fFileNames[j];
-            if (filename) {
-                if (!haveSystemFont(filename)) {
-                    SkDebugf("---- skipping fallback font %s because it was not "
-                            "previously loaded as a system font", filename);
-                    continue;
-                }
-
-                // ensure the fallback font exists before adding it to the list
-                bool isFixedWidth;
-                SkString name;
-                SkTypeface::Style style;
-                if (!getNameAndStyle(filename, &name, &style,
-                                        &isFixedWidth, false)) {
-                    continue;
-                }
-
-                SkFontID uniqueID = findUniqueIDLocked(filename);
-                SkASSERT(uniqueID != 0);
-                if (findFallbackFontIndex(uniqueID) >= 0) {
-                    SkDebugf("---- system font and fallback font files specify a duplicate "
-                            "font %s, skipping the second occurrence", filename);
-                    continue;
-                }
-
-                SkDEBUGF(("---- reload %s as fallback[%d] fontID %d\n",
-                          filename, gFallbackFonts.count(), uniqueID));
-
-                *gFallbackFonts.append() = uniqueID;
-                break;  // The fallback set contains only the first font of each family
-            }
-        }
-    }
-
-    fallbackFamilies.deleteAll();
-}
-
 static void loadSystemFontsLocked() {
-#if !defined(SK_BUILD_FOR_ANDROID_NDK)
-    static char prevLanguage[3];
-    static char prevRegion[3];
-    char language[3] = "";
-    char region[3] = "";
-
-    getLocale(language, region);
-
-    if (!gDefaultNormal) {
-        strncpy(prevLanguage, language, 2);
-        strncpy(prevRegion, region, 2);
-        initSystemFontsLocked();
-    } else if (strncmp(language, prevLanguage, 2) || strncmp(region, prevRegion, 2)) {
-        strncpy(prevLanguage, language, 2);
-        strncpy(prevRegion, region, 2);
-        reloadFallbackFontsLocked();
-    }
-#else
     if (!gDefaultNormal) {
         initSystemFontsLocked();
-        reloadFallbackFontsLocked();
     }
-#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -883,16 +997,20 @@
     }
 }
 
-SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+SkFontID SkFontHost::NextLogicalFont(const SkScalerContext::Rec& rec) {
     SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
-    return nextLogicalFontLocked(currFontID, origFontID);
+    return nextLogicalFontLocked(rec);
 }
 
-static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID) {
+static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) {
     loadSystemFontsLocked();
 
-    const SkTypeface* origTypeface = findFromUniqueIDLocked(origFontID);
-    const SkTypeface* currTypeface = findFromUniqueIDLocked(currFontID);
+    const SkTypeface* origTypeface = findFromUniqueIDLocked(rec.fOrigFontID);
+    const SkTypeface* currTypeface = findFromUniqueIDLocked(rec.fFontID);
+
+    FallbackFontList* currentFallbackList =
+            getFallbackFontListLocked(rec.fLanguage);
+    SkASSERT(currentFallbackList);
 
     SkASSERT(origTypeface != 0);
     SkASSERT(currTypeface != 0);
@@ -906,19 +1024,32 @@
         in our list. Note: list is zero-terminated, and returning zero means
         we have no more fonts to use for fallbacks.
      */
-    int plainFallbackFontIndex = findFallbackFontIndex(plainFontID);
+    int plainFallbackFontIndex = findFallbackFontIndex(plainFontID, currentFallbackList);
     int nextFallbackFontIndex = plainFallbackFontIndex + 1;
-    SkFontID nextFontID;
-    if (nextFallbackFontIndex == gFallbackFonts.count()) {
-        nextFontID = 0; // no more fallbacks
-    } else {
-        const SkTypeface* nextTypeface = findFromUniqueIDLocked(gFallbackFonts[nextFallbackFontIndex]);
-        nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID();
+
+    // If a rec object is set to prefer "kDefault_Variant" it means they have no preference
+    // In this case, we set the value to "kCompact_Variant"
+    SkPaint::FontVariant recPreference = rec.fFontVariant;
+    if (recPreference == SkPaint::kDefault_Variant) {
+        recPreference = SkPaint::kCompact_Variant;
+    }
+    SkFontID nextFontID = 0;
+    while (nextFallbackFontIndex < currentFallbackList->fList.count()) {
+        bool normalFont =
+                (currentFallbackList->fList[nextFallbackFontIndex].fVariant == SkPaint::kDefault_Variant);
+        bool fontChosen = (currentFallbackList->fList[nextFallbackFontIndex].fVariant == recPreference);
+        if (normalFont || fontChosen) {
+            const SkTypeface* nextTypeface =
+                    findFromUniqueIDLocked(currentFallbackList->fList[nextFallbackFontIndex].fFontID);
+            nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID();
+            break;
+        }
+        nextFallbackFontIndex++;
     }
 
     SkDEBUGF(("---- nextLogicalFont: currFontID=%d, origFontID=%d, plainFontID=%d, "
             "plainFallbackFontIndex=%d, nextFallbackFontIndex=%d "
-            "=> nextFontID=%d", currFontID, origFontID, plainFontID,
+            "=> nextFontID=%d", rec.fFontID, rec.fOrigFontID, plainFontID,
             plainFallbackFontIndex, nextFallbackFontIndex, nextFontID));
     return nextFontID;
 }
@@ -958,3 +1089,143 @@
     stream->unref();
     return face;
 }
+
+///////////////////////////////////////////////////////////////////////////////
+// Function from SkTypeface_android.h
+///////////////////////////////////////////////////////////////////////////////
+
+static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant) {
+    SkTypeface* face = FindBestFace(gDefaultFamily, style);
+    if (!face) {
+        return 0;
+    }
+
+    SkPaint paint;
+    paint.setTypeface(face);
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    paint.setFontVariant(fontVariant);
+
+    SkAutoGlyphCache autoCache(paint, NULL);
+    SkGlyphCache*    cache = autoCache.getCache();
+    SkFontID         fontID = 0;
+
+    SkScalerContext* ctx = cache->getScalerContext();
+    if (ctx) {
+        return ctx->findTypefaceIdForChar(uni);
+    }
+    return 0;
+}
+
+struct HB_UnicodeMapping {
+    HB_Script script;
+    const SkUnichar unicode;
+};
+
+/*
+ * The following scripts are not complex fonts and we do not expect them to be parsed by this table
+ * HB_Script_Common,
+ * HB_Script_Greek,
+ * HB_Script_Cyrillic,
+ * HB_Script_Hangul
+ * HB_Script_Inherited
+ */
+
+static HB_UnicodeMapping HB_UnicodeMappingArray[] {
+    {HB_Script_Armenian,      0x0531},
+    {HB_Script_Hebrew,        0x0591},
+    {HB_Script_Arabic,        0x0600},
+    {HB_Script_Syriac,        0x0710},
+    {HB_Script_Thaana,        0x0780},
+    {HB_Script_Nko,           0x07C0},
+    {HB_Script_Devanagari,    0x0901},
+    {HB_Script_Bengali,       0x0981},
+    {HB_Script_Gurmukhi,      0x0A10},
+    {HB_Script_Gujarati,      0x0A90},
+    {HB_Script_Oriya,         0x0B10},
+    {HB_Script_Tamil,         0x0B82},
+    {HB_Script_Telugu,        0x0C10},
+    {HB_Script_Kannada,       0x0C90},
+    {HB_Script_Malayalam,     0x0D10},
+    {HB_Script_Sinhala,       0x0D90},
+    {HB_Script_Thai,          0x0E01},
+    {HB_Script_Lao,           0x0E81},
+    {HB_Script_Tibetan,       0x0F00},
+    {HB_Script_Myanmar,       0x1000},
+    {HB_Script_Georgian,      0x10A0},
+    // we don't currently support HB_Script_Ethiopic, it is a placeholder for an upstream merge
+    //{HB_Script_Ethiopic,    0x1200},
+    {HB_Script_Ogham,         0x1680},
+    {HB_Script_Runic,         0x16A0},
+    {HB_Script_Khmer,         0x1780},
+};
+
+// returns 0 for "Not Found"
+static SkUnichar getUnicodeFromHBScript(HB_Script script) {
+    SkUnichar unichar = 0;
+    int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
+    for (int i = 0; i < numSupportedFonts; i++) {
+        if (script == HB_UnicodeMappingArray[i].script) {
+            unichar = HB_UnicodeMappingArray[i].unicode;
+            break;
+        }
+    }
+    return unichar;
+}
+
+struct TypefaceLookupStruct {
+    HB_Script            script;
+    SkTypeface::Style    style;
+    SkPaint::FontVariant fontVariant;
+    SkTypeface*          typeface;
+};
+
+SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex);  // This is the mutex for gTypefaceTable
+static SkTDArray<TypefaceLookupStruct> gTypefaceTable;  // This is protected by gTypefaceTableMutex
+
+static int typefaceLookupCompare(const TypefaceLookupStruct& first,
+        const TypefaceLookupStruct& second) {
+    if (first.script != second.script) {
+        return (first.script > second.script) ? 1 : -1;
+    }
+    if (first.style != second.style) {
+        return (first.style > second.style) ? 1 : -1;
+    }
+    if (first.fontVariant != second.fontVariant) {
+        return (first.fontVariant > second.fontVariant) ? 1 : -1;
+    }
+    return 0;
+}
+
+SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+        SkPaint::FontVariant fontVariant) {
+    SkTypeface* retTypeface = NULL;
+
+    SkAutoMutexAcquire ac(gTypefaceTableMutex); // Note: NOT gFamilyHeadAndNameListMutex
+    TypefaceLookupStruct key;
+    key.script = script;
+    key.style = style;
+    key.fontVariant = fontVariant;
+    int index = SkTSearch<TypefaceLookupStruct>(
+            (const TypefaceLookupStruct*) gTypefaceTable.begin(),
+            gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct),
+            &typefaceLookupCompare);
+    if (index >= 0) {
+        retTypeface = gTypefaceTable[index].typeface;
+    }
+    else {
+        SkUnichar unichar = getUnicodeFromHBScript(script);
+        if (!unichar) {
+            return NULL;
+        }
+        SkFontID newFontID = findFontIDForChar(unichar, style, fontVariant);
+        // retrieve the typeface that corresponds to this fontID
+        retTypeface = FindFromUniqueID(newFontID);
+        key.typeface = retTypeface;
+        index = ~index;
+        *gTypefaceTable.insert(index) = key;
+    }
+    // we ref(), the caller is expected to unref when they are done
+    SkSafeRef(retTypeface);
+    return retTypeface;
+}