am f6067133: am 5f421caf: (-s ours) am c2ad1992: Merge "Forward-compatibility stubs"

* commit 'f6067133f37c4b3f7ec653032b1432506be758fd':
  Forward-compatibility stubs
diff --git a/Android.mk b/Android.mk
index 846c157..c240d69 100644
--- a/Android.mk
+++ b/Android.mk
@@ -108,6 +108,7 @@
 	src/core/SkGeometry.cpp \
 	src/core/SkGlyphCache.cpp \
 	src/core/SkGraphics.cpp \
+	src/core/SkLanguage.cpp \
 	src/core/SkLineClipper.cpp \
 	src/core/SkMallocPixelRef.cpp \
 	src/core/SkMask.cpp \
diff --git a/include/core/SkLanguage.h b/include/core/SkLanguage.h
new file mode 100644
index 0000000..923008e
--- /dev/null
+++ b/include/core/SkLanguage.h
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkLanguage_DEFINED
+#define SkLanguage_DEFINED
+
+#include "SkTypes.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+
+#include "SkString.h"
+
+struct SkLanguageInfo {
+    SkLanguageInfo(const char* tag) : fTag(tag) { }
+    SkString fTag; //! BCP 47 language identifier
+};
+
+/** \class SkLanguage
+
+    The SkLanguage class represents a human written language, and is used by
+    text draw operations to determine which glyph to draw when drawing
+    characters with variants (ie Han-derived characters).
+*/
+class SkLanguage {
+public:
+    SkLanguage() : fInfo(getInfo("")) { }
+    SkLanguage(const char* tag) : fInfo(getInfo(tag)) { }
+    SkLanguage(const SkLanguage& b) : fInfo(b.fInfo) { }
+
+    /** Gets a BCP 47 language identifier for this SkLanguage.
+        @return a BCP 47 language identifier representing this language
+    */
+    const SkString& getTag() const { return fInfo->fTag; }
+
+    /** Performs BCP 47 fallback to return an SkLanguage one step more general.
+        @return an SkLanguage one step more general
+    */
+    SkLanguage getParent() const;
+
+    bool operator==(const SkLanguage& b) const {
+        return fInfo == b.fInfo;
+    }
+    bool operator!=(const SkLanguage& b) const {
+        return fInfo != b.fInfo;
+    }
+    bool operator<(const SkLanguage& b) const {
+        return fInfo < b.fInfo;
+    }
+    bool operator>(const SkLanguage& b) const {
+        return fInfo > b.fInfo;
+    }
+    SkLanguage& operator=(const SkLanguage& b) {
+        fInfo = b.fInfo;
+        return *this;
+    }
+
+private:
+    const SkLanguageInfo* fInfo;
+
+    static const SkLanguageInfo* getInfo(const char* tag);
+};
+
+#endif // #ifdef SK_BUILD_FOR_ANDROID
+#endif // #ifndef SkLanguage_DEFINED
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 1715013..d2233f0 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -10,11 +10,16 @@
 #ifndef SkPaint_DEFINED
 #define SkPaint_DEFINED
 
+#include "SkTypes.h"
 #include "SkColor.h"
 #include "SkDrawLooper.h"
 #include "SkXfermode.h"
 #include "SkString.h"
 
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkLanguage.h"
+#endif
+
 class SkAutoGlyphCache;
 class SkColorFilter;
 class SkDescriptor;
@@ -656,15 +661,15 @@
     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; }
+    const SkLanguage& getLanguage() const { return fLanguage; }
 
-    /** Set the paint's text locale.
-        @param locale set the paint's locale value for drawing text.
+    /** Set the paint's language value used for drawing text.
+        @param language set the paint's language value for drawing text.
     */
-    void    setTextLocale(const SkString& locale);
+    void setLanguage(const SkLanguage& language);
 
 
     enum FontVariant {
@@ -924,7 +929,7 @@
     unsigned        fTextEncoding : 2;  // 3 values
     unsigned        fHinting : 2;
 #ifdef SK_BUILD_FOR_ANDROID
-    SkString        fTextLocale;
+    SkLanguage      fLanguage;
     FontVariant     fFontVariant;
 #endif
 
diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h
index 33c3c3d..4a0c70d 100644
--- a/include/core/SkScalerContext.h
+++ b/include/core/SkScalerContext.h
@@ -17,6 +17,10 @@
 #include "SkPoint.h"
 #include "SkTypeface.h"
 
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkLanguage.h"
+#endif
+
 //#define SK_USE_COLOR_LUMINANCE
 
 class SkDescriptor;
@@ -211,6 +215,7 @@
         uint32_t    fLumBits;
 #endif
 #ifdef SK_BUILD_FOR_ANDROID
+        SkLanguage fLanguage;
         SkPaint::FontVariant fFontVariant;
 #endif
         uint8_t     fMaskFormat;
@@ -304,16 +309,7 @@
 #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) {
-        SkScalerContext* ctx = this;
-        while (NULL != ctx) {
-            if (ctx->generateCharToGlyph(uni)) {
-                return ctx->fRec.fFontID;
-            }
-            ctx = ctx->getNextContext();
-        }
-        return 0;
-    }
+    SkFontID findTypefaceIdForChar(SkUnichar uni);
 
     unsigned getBaseGlyphCount(SkUnichar charCode);
 #endif
@@ -341,6 +337,8 @@
     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
 
 private:
+    SkScalerContext* getContextFromChar(SkUnichar uni, unsigned& glyphID);
+
     SkPathEffect*   fPathEffect;
     SkMaskFilter*   fMaskFilter;
     SkRasterizer*   fRasterizer;
diff --git a/src/core/SkLanguage.cpp b/src/core/SkLanguage.cpp
new file mode 100644
index 0000000..3b8ba3c
--- /dev/null
+++ b/src/core/SkLanguage.cpp
@@ -0,0 +1,54 @@
+
+/*
+ * Copyright 2012 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkLanguage.h"
+
+#ifdef SK_BUILD_FOR_ANDROID // currently only for Android
+
+#include "SkTDict.h"
+#include "SkThread.h"
+#include <cstring>
+
+SkLanguage SkLanguage::getParent() const {
+    SkASSERT(fInfo != NULL);
+    SkASSERT(fInfo->fTag != NULL);
+    const char* tag = fInfo->fTag.c_str();
+    SkASSERT(tag != NULL);
+
+    // strip off the rightmost "-.*"
+    char* parentTagEnd = strrchr(tag, '-');
+    if (parentTagEnd == NULL) {
+        return SkLanguage("");
+    }
+    size_t parentTagLen = parentTagEnd - tag;
+    char parentTag[parentTagLen + 1];
+    strncpy(parentTag, tag, parentTagLen);
+    parentTag[parentTagLen] = '\0';
+    return SkLanguage(parentTag);
+}
+
+SK_DECLARE_STATIC_MUTEX(gGetInfoMutex);
+const SkLanguageInfo* SkLanguage::getInfo(const char* tag) {
+    SkAutoMutexAcquire lock(gGetInfoMutex);
+
+    static const size_t kDictSize = 128;
+    static SkTDict<SkLanguageInfo*> tagToInfo(kDictSize);
+
+    // try a lookup
+    SkLanguageInfo* info;
+    if (tagToInfo.find(tag, &info)) {
+        return info;
+    }
+
+    // no match - add this language
+    info = new SkLanguageInfo(tag);
+    tagToInfo.set(tag, info);
+    return info;
+}
+
+#endif
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 412ab2b..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,7 @@
     fTextEncoding = kUTF8_TextEncoding;
     fHinting    = SkPaintDefaults_Hinting;
 #ifdef SK_BUILD_FOR_ANDROID
-    new(&fTextLocale) SkString();
+    fLanguage = SkLanguage();
     fFontVariant = kDefault_Variant;
     fGenerationID = 0;
 #endif
@@ -89,9 +90,6 @@
     SkSafeRef(fRasterizer);
     SkSafeRef(fLooper);
     SkSafeRef(fImageFilter);
-#ifdef SK_BUILD_FOR_ANDROID
-    new(&fTextLocale) SkString(src.fTextLocale);
-#endif
 }
 
 SkPaint::~SkPaint() {
@@ -130,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
 
@@ -367,9 +363,9 @@
 }
 
 #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;
     }
 }
@@ -1575,6 +1571,7 @@
     rec->setLuminanceBits(computeLuminance(paint));
 #endif
 #ifdef SK_BUILD_FOR_ANDROID
+    rec->fLanguage = paint.getLanguage();
     rec->fFontVariant = paint.getFontVariant();
 #endif //SK_BUILD_FOR_ANDROID
 
@@ -1852,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) {
@@ -1906,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 e33ad7a..85baf94 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -162,6 +162,22 @@
     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) {
+            SkDebugf("--- no context for char %x\n", uni);
+            return NULL;
+        }
+    }
+    return ctx;
+}
+
 SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
     unsigned glyphID = glyph.getGlyphID();
     SkScalerContext* ctx = this;
@@ -182,6 +198,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.
 
@@ -189,21 +215,13 @@
     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 {
+        return this->fBaseGlyphCount;
     }
-    return ctx->fBaseGlyphCount;
 }
 #endif
 
@@ -211,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 b2aea24..5b630c7 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -146,16 +146,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;
 }
 
@@ -211,8 +215,8 @@
 
 static bool webp_get_config_resize(WebPDecoderConfig& config,
                                    SkBitmap* decodedBitmap,
-                                   int width, int height) {
-    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
+                                   int width, int height, int hasAlpha) {
+    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha);
     if (mode == MODE_LAST) {
         return false;
     }
@@ -239,10 +243,12 @@
 
 static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
                                         SkBitmap* decodedBitmap,
-                                        SkIRect region) {
+                                        SkIRect region, int hasAlpha) {
 
-    if (!webp_get_config_resize(config, decodedBitmap,
-                                region.width(), region.height())) return false;
+    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;
@@ -360,7 +366,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;
     }
 
@@ -418,7 +424,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;
     }
 
@@ -560,7 +567,7 @@
 static SkImageDecoder* DFactory(SkStream* stream) {
     int width, height, hasAlpha;
     if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
-        return false;
+        return NULL;
     }
 
     // Magic matches, call decoder
diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp
index 9296769..420ad1c 100644
--- a/src/ports/FontHostConfiguration_android.cpp
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -16,7 +16,7 @@
 */
 
 #include "FontHostConfiguration_android.h"
-#include "SkString.h"
+#include "SkLanguage.h"
 #include "SkTDArray.h"
 #include "SkTypeface.h"
 #include <expat.h>
@@ -28,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.
@@ -82,8 +81,8 @@
 }
 
 /**
- * Handler for font files. This processes the attributes for language and variants
- * then lets textHandler handle the actual file name
+ * 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();
@@ -100,10 +99,8 @@
                 } else if (strncmp(attributeValue, "compact", valueLength) == 0) {
                     newFileInfo->fVariant = SkPaint::kCompact_Variant;
                 }
-            } else if (strncmp(attributeName, "language", nameLength) == 0) {
-                if (strncmp(attributeValue, "ja", valueLength) == 0) {
-                    newFileInfo->fLanguage = "ja";
-                }  //else if (other languages)
+            } else if (strncmp(attributeName, "lang", nameLength) == 0) {
+                newFileInfo->fLanguage = SkLanguage(attributeValue);
             }
             //each element is a pair of attributeName/attributeValue string pairs
             currentAttributeIndex += 2;
@@ -170,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.
@@ -238,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) {
@@ -254,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 c8a70e3..6734b08 100644
--- a/src/ports/FontHostConfiguration_android.h
+++ b/src/ports/FontHostConfiguration_android.h
@@ -17,17 +17,22 @@
 #ifndef FONTHOSTCONFIGURATION_ANDROID_H_
 #define FONTHOSTCONFIGURATION_ANDROID_H_
 
-#include "SkTDArray.h"
+#include "SkTypes.h"
+
+#include "SkLanguage.h"
 #include "SkPaint.h"
+#include "SkTDArray.h"
 
 struct FontFileInfo {
-    FontFileInfo() : fVariant(SkPaint::kDefault_Variant), fLanguage(NULL), fFileName(NULL) {}
+    FontFileInfo() : fFileName(NULL), fVariant(SkPaint::kDefault_Variant),
+            fLanguage() {
+    }
+
     const char*          fFileName;
     SkPaint::FontVariant fVariant;
-    const char*          fLanguage;  // We may eventually use a enum for this
+    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
@@ -54,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_android.cpp b/src/ports/SkFontHost_android.cpp
index 38d1d06..dddadd0 100644
--- a/src/ports/SkFontHost_android.cpp
+++ b/src/ports/SkFontHost_android.cpp
@@ -28,7 +28,10 @@
 #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
@@ -79,6 +82,7 @@
 static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec);
 static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream);
 
+
 ///////////////////////////////////////////////////////////////////////////////
 
 struct FamilyRec;
@@ -414,6 +418,7 @@
     const char*          fFileName;
     const char* const*   fNames;     // null-terminated list
     SkPaint::FontVariant fVariant;
+    SkLanguage           fLanguage;
 };
 
 //used to record information about the fallback fonts
@@ -422,22 +427,28 @@
     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<FallbackFontRec> 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,
@@ -458,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);
@@ -504,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);
 
@@ -525,6 +694,7 @@
             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
@@ -549,7 +719,7 @@
             } else {
                 fontInfoRecord.fNames = NULL;
             }
-            *gSystemFonts.append() = fontInfoRecord;
+            gSystemFonts.push_back(fontInfoRecord);
         }
     }
     fontFamilies.deleteAll();
@@ -560,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.
@@ -575,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
@@ -615,12 +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()));
-                FallbackFontRec newFallbackRec;
-                newFallbackRec.fFontID = tf->uniqueID();
-                newFallbackRec.fVariant = gSystemFonts[i].fVariant;
-                *gFallbackFonts.append() = newFallbackRec;
+                // add to appropriate fallback chains
+                FallbackFontRec fallbackRec;
+                fallbackRec.fFontID = tf->uniqueID();
+                fallbackRec.fVariant = gSystemFonts[i].fVariant;
+                addFallbackFontLocked(fallbackRec, gSystemFonts[i].fLanguage);
             }
 
             firstInFamily = tf;
@@ -637,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
@@ -645,102 +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].fFontID == 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->fFontFileArray.count(); ++j) {
-            const char* filename = family->fFontFileArray[j]->fFileName;
-            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));
-                FallbackFontRec newFallbackFont;
-                newFallbackFont.fFontID = uniqueID;
-                newFallbackFont.fVariant = family->fFontFileArray[j]->fVariant;
-                *gFallbackFonts.append() = newFallbackFont;
-                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
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -924,6 +1008,10 @@
     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);
 
@@ -936,7 +1024,7 @@
         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;
 
     // If a rec object is set to prefer "kDefault_Variant" it means they have no preference
@@ -946,13 +1034,13 @@
         recPreference = SkPaint::kCompact_Variant;
     }
     SkFontID nextFontID = 0;
-    while (nextFallbackFontIndex < gFallbackFonts.count()) {
+    while (nextFallbackFontIndex < currentFallbackList->fList.count()) {
         bool normalFont =
-                (gFallbackFonts[nextFallbackFontIndex].fVariant == SkPaint::kDefault_Variant);
-        bool fontChosen = (gFallbackFonts[nextFallbackFontIndex].fVariant == recPreference);
+                (currentFallbackList->fList[nextFallbackFontIndex].fVariant == SkPaint::kDefault_Variant);
+        bool fontChosen = (currentFallbackList->fList[nextFallbackFontIndex].fVariant == recPreference);
         if (normalFont || fontChosen) {
             const SkTypeface* nextTypeface =
-                    findFromUniqueIDLocked(gFallbackFonts[nextFallbackFontIndex].fFontID);
+                    findFromUniqueIDLocked(currentFallbackList->fList[nextFallbackFontIndex].fFontID);
             nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID();
             break;
         }