Android specific changes that are upstreamed from the android framework.

R=reed@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@9283 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkPaintOptionsAndroid.h b/include/core/SkPaintOptionsAndroid.h
index 23e3d0d..7a2ea8f 100644
--- a/include/core/SkPaintOptionsAndroid.h
+++ b/include/core/SkPaintOptionsAndroid.h
@@ -10,10 +10,10 @@
 #ifndef SkPaintOptionsAndroid_DEFINED
 #define SkPaintOptionsAndroid_DEFINED
 
-#ifdef SK_BUILD_FOR_ANDROID
-
-#include "SkString.h"
 #include "SkTypes.h"
+#include "SkString.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
 
 class SkFlattenableReadBuffer;
 class SkFlattenableWriteBuffer;
diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h
index fa702cb..655670f 100644
--- a/include/ports/SkTypeface_android.h
+++ b/include/ports/SkTypeface_android.h
@@ -9,10 +9,10 @@
 #ifndef SkTypeface_android_DEFINED
 #define SkTypeface_android_DEFINED
 
-#ifdef SK_BUILD_FOR_ANDROID
-
 #include "SkTypeface.h"
 
+#ifdef SK_BUILD_FOR_ANDROID
+
 class SkPaintOptionsAndroid;
 
 /**
@@ -47,4 +47,26 @@
                                          const SkPaintOptionsAndroid& options);
 
 #endif // #ifdef SK_BUILD_FOR_ANDROID
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+
+#include "SkPaintOptionsAndroid.h"
+#include "../harfbuzz/src/harfbuzz-shaper.h"
+#include "../harfbuzz_ng/src/hb.h"
+
+/**
+ *  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* SkCreateTypefaceForScriptNG(hb_script_t script, SkTypeface::Style style,
+        SkPaintOptionsAndroid::FontVariant fontVariant = SkPaintOptionsAndroid::kDefault_Variant);
+
+SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+        SkPaintOptionsAndroid::FontVariant fontVariant = SkPaintOptionsAndroid::kDefault_Variant);
+
+#endif // #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
 #endif // #ifndef SkTypeface_android_DEFINED
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index f673959..9b6b883 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -101,6 +101,10 @@
     SkSafeRef(fLooper);
     SkSafeRef(fImageFilter);
     SkSafeRef(fAnnotation);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
+#endif
 }
 
 SkPaint::~SkPaint() {
@@ -915,12 +919,12 @@
 public:
     SkAutoRestorePaintTextSizeAndFrame(const SkPaint* paint)
             : fPaint((SkPaint*)paint) {
-        fTextSize = paint->getTextSize();
-        fStyle = paint->getStyle();
-        fPaint->setStyle(SkPaint::kFill_Style);
 #ifdef SK_BUILD_FOR_ANDROID
         fGenerationID = fPaint->getGenerationID();
 #endif
+        fTextSize = paint->getTextSize();
+        fStyle = paint->getStyle();
+        fPaint->setStyle(SkPaint::kFill_Style);
     }
 
     ~SkAutoRestorePaintTextSizeAndFrame() {
diff --git a/src/core/SkPaintOptionsAndroid.cpp b/src/core/SkPaintOptionsAndroid.cpp
index 46e8060..31f489c 100644
--- a/src/core/SkPaintOptionsAndroid.cpp
+++ b/src/core/SkPaintOptionsAndroid.cpp
@@ -6,14 +6,14 @@
  * found in the LICENSE file.
  */
 
-#ifdef SK_BUILD_FOR_ANDROID
-
 #include "SkPaintOptionsAndroid.h"
 #include "SkFlattenableBuffers.h"
 #include "SkTDict.h"
 #include "SkThread.h"
 #include <cstring>
 
+#ifdef SK_BUILD_FOR_ANDROID
+
 SkLanguage SkLanguage::getParent() const {
     SkASSERT(!fTag.isEmpty());
     const char* tag = fTag.c_str();
diff --git a/src/ports/SkFontConfigInterface_android.cpp b/src/ports/SkFontConfigInterface_android.cpp
index 6cf7507..8039906 100644
--- a/src/ports/SkFontConfigInterface_android.cpp
+++ b/src/ports/SkFontConfigInterface_android.cpp
@@ -14,7 +14,6 @@
 #include "SkFontMgr.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
-#include "SkPaintOptionsAndroid.h"
 #include "SkString.h"
 #include "SkStream.h"
 #include "SkThread.h"
@@ -45,6 +44,12 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+typedef int32_t FontRecID;
+#define INVALID_FONT_REC_ID -1
+
+typedef int32_t FamilyRecID;
+#define INVALID_FAMILY_REC_ID -1
+
 // used to record our notion of the pre-existing fonts
 struct FontRec {
     SkRefPtr<SkTypeface> fTypeface;
@@ -53,11 +58,9 @@
     SkPaintOptionsAndroid fPaintOptions;
     bool fIsFallbackFont;
     bool fIsValid;
+    FamilyRecID fFamilyRecID;
 };
 
-typedef int32_t FontRecID;
-#define INVALID_FONT_REC_ID -1
-
 struct FamilyRec {
     FamilyRec() {
         memset(fFontRecID, INVALID_FONT_REC_ID, sizeof(fFontRecID));
@@ -67,8 +70,6 @@
     FontRecID fFontRecID[FONT_STYLE_COUNT];
 };
 
-typedef int32_t FamilyRecID;
-#define INVALID_FAMILY_REC_ID -1
 
 typedef SkTDArray<FontRecID> FallbackFontList;
 
@@ -105,7 +106,8 @@
 
 private:
     void addFallbackFont(FontRecID fontRecID);
-    FallbackFontList* findFallbackFontList(const SkLanguage& lang);
+    SkTypeface* getTypefaceForFontRec(FontRecID fontRecID);
+    FallbackFontList* findFallbackFontList(const SkLanguage& lang, bool isOriginal = true);
 
     SkTArray<FontRec> fFonts;
     SkTArray<FamilyRec> fFontFamilies;
@@ -114,6 +116,7 @@
 
     // (SkLanguage)<->(fallback chain index) translation
     SkTDict<FallbackFontList*> fFallbackFontDict;
+    SkTDict<FallbackFontList*> fFallbackFontAliasDict;
     FallbackFontList fDefaultFallbackList;
 };
 
@@ -189,7 +192,8 @@
         fFontFamilies(fontFamilies.count() / FamilyRec::FONT_STYLE_COUNT),
         fFamilyNameDict(1024),
         fDefaultFamilyRecID(INVALID_FAMILY_REC_ID),
-        fFallbackFontDict(128) {
+        fFallbackFontDict(128),
+        fFallbackFontAliasDict(128) {
 
     for (int i = 0; i < fontFamilies.count(); ++i) {
         FontFamily* family = fontFamilies[i];
@@ -215,6 +219,7 @@
             fontRec.fPaintOptions = family->fFontFiles[j]->fPaintOptions;
             fontRec.fIsFallbackFont = family->fIsFallbackFont;
             fontRec.fIsValid = false;
+            fontRec.fFamilyRecID = familyRecID;
 
             const FontRecID fontRecID = fFonts.count() - 1;
 
@@ -244,6 +249,7 @@
             if (familyRec == NULL) {
                 familyRec = &fFontFamilies.push_back();
                 familyRecID = fFontFamilies.count() - 1;
+                fontRec.fFamilyRecID = familyRecID;
             }
 
             // add this font to the current familyRec
@@ -454,13 +460,26 @@
     return false;
 }
 
-static SkTypeface* get_typeface_for_rec(FontRec& fontRec) {
+static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
+    const FontRecID* fontRecID = (const FontRecID*)ctx;
+    FontRecID currFontRecID = ((FontConfigTypeface*)face)->getIdentity().fID;
+    return currFontRecID == *fontRecID;
+}
+
+SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForFontRec(FontRecID fontRecID) {
+    FontRec& fontRec = fFonts[fontRecID];
     SkTypeface* face = fontRec.fTypeface.get();
     if (!face) {
-        // TODO look for it in the typeface cache
+        // look for it in the typeface cache
+        face = SkTypefaceCache::FindByProcAndRef(find_proc, &fontRecID);
 
         // if it is not in the cache then create it
-        face = SkTypeface::CreateFromFile(fontRec.fFileName.c_str());
+        if (!face) {
+            const char* familyName = NULL;
+            SkAssertResult(fFamilyNameDict.findKey(fontRec.fFamilyRecID, &familyName));
+            SkASSERT(familyName);
+            face = SkTypeface::CreateFromName(familyName, fontRec.fStyle);
+        }
 
         // store the result for subsequent lookups
         fontRec.fTypeface = face;
@@ -472,7 +491,7 @@
 bool SkFontConfigInterfaceAndroid::getFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
     for (int i = 0; i < fDefaultFallbackList.count(); i++) {
         FontRecID fontRecID = fDefaultFallbackList[i];
-        SkTypeface* face = get_typeface_for_rec(fFonts[fontRecID]);
+        SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
 
         SkPaint paint;
         paint.setTypeface(face);
@@ -492,7 +511,7 @@
                                                              SkTypeface::Style style,
                                                              SkPaintOptionsAndroid::FontVariant fontVariant) {
     FontRecID fontRecID = find_best_style(fFontFamilies[fDefaultFamilyRecID], style);
-    SkTypeface* face = get_typeface_for_rec(fFonts[fontRecID]);
+    SkTypeface* face = this->getTypefaceForFontRec(fontRecID);
 
     SkPaintOptionsAndroid paintOptions;
     paintOptions.setFontVariant(fontVariant);
@@ -514,22 +533,29 @@
     return NULL;
 }
 
-FallbackFontList* SkFontConfigInterfaceAndroid::findFallbackFontList(const SkLanguage& lang) {
+FallbackFontList* SkFontConfigInterfaceAndroid::findFallbackFontList(const SkLanguage& lang,
+                                                                     bool isOriginal) {
     const SkString& langTag = lang.getTag();
     if (langTag.isEmpty()) {
         return &fDefaultFallbackList;
     }
 
     FallbackFontList* fallbackFontList;
-    if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) {
+    if (fFallbackFontDict.find(langTag.c_str(), langTag.size(), &fallbackFontList) ||
+        fFallbackFontAliasDict.find(langTag.c_str(), langTag.size(), &fallbackFontList)) {
         return fallbackFontList;
     }
 
     // attempt a recursive fuzzy match
-    // TODO we could cache the intermediate parent so that we don't have to do
-    //      the recursion again.
     SkLanguage parent = lang.getParent();
-    return findFallbackFontList(parent);
+    fallbackFontList = findFallbackFontList(parent, false);
+
+    // cache the original lang so we don't have to do the recursion again.
+    if (isOriginal) {
+        DEBUG_FONT(("----  Created fallback list alias for \"%s\"", langTag.c_str()));
+        fFallbackFontAliasDict.set(langTag.c_str(), fallbackFontList);
+    }
+    return fallbackFontList;
 }
 
 SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontID,
@@ -544,21 +570,20 @@
     }
 
     const SkTypeface* currTypeface = SkTypefaceCache::FindByID(currFontID);
+    SkASSERT(currTypeface != 0);
 
     FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage());
     SkASSERT(currentFallbackList);
 
-    SkASSERT(currTypeface != 0);
-
     // we must convert currTypeface into a FontRecID
     FontRecID currFontRecID = ((FontConfigTypeface*)currTypeface)->getIdentity().fID;
+    SkASSERT(INVALID_FONT_REC_ID != currFontRecID);
 
     // TODO lookup the index next font in the chain
     int currFallbackFontIndex = currentFallbackList->find(currFontRecID);
     int nextFallbackFontIndex = currFallbackFontIndex + 1;
-    SkASSERT(-1 == nextFallbackFontIndex);
 
-    if(nextFallbackFontIndex >= currentFallbackList->count() && -1 == currFallbackFontIndex) {
+    if(nextFallbackFontIndex >= currentFallbackList->count()) {
         return NULL;
     }
 
@@ -574,17 +599,17 @@
     SkTypeface* nextLogicalTypeface = 0;
     while (nextFallbackFontIndex < currentFallbackList->count()) {
         FontRecID fontRecID = currentFallbackList->getAt(nextFallbackFontIndex);
-        if (fFonts[fontRecID].fPaintOptions.getFontVariant() & acceptedVariants) {
-            nextLogicalTypeface = get_typeface_for_rec(fFonts[fontRecID]);
+        if ((fFonts[fontRecID].fPaintOptions.getFontVariant() & acceptedVariants) != 0) {
+            nextLogicalTypeface = this->getTypefaceForFontRec(fontRecID);
             break;
         }
         nextFallbackFontIndex++;
     }
 
     DEBUG_FONT(("---- nextLogicalFont: currFontID=%d, origFontID=%d, currRecID=%d, "
-                "lang=%s, variant=%d, nextFallbackIndex=%d => nextLogicalTypeface=%d",
+                "lang=%s, variant=%d, nextFallbackIndex[%d,%d] => nextLogicalTypeface=%d",
                 currFontID, origFontID, currFontRecID, opts.getLanguage().getTag().c_str(),
-                variant, nextFallbackFontIndex,
+                variant, nextFallbackFontIndex, currentFallbackList->getAt(nextFallbackFontIndex),
                 (nextLogicalTypeface) ? nextLogicalTypeface->uniqueID() : 0));
     return SkSafeRef(nextLogicalTypeface);
 }
@@ -617,6 +642,157 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+
+struct HB_UnicodeMapping {
+    // TODO: when the WebView no longer needs harfbuzz_old, remove
+    HB_Script script_old;
+    hb_script_t 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
+ */
+
+/* Harfbuzz (old) is missing a number of scripts in its table. For these,
+ * we include a value which can never happen. We won't get complex script
+ * shaping in these cases, but the library wouldn't know how to shape
+ * them anyway. */
+#define HB_Script_Unknown HB_ScriptCount
+
+static HB_UnicodeMapping HB_UnicodeMappingArray[] = {
+    {HB_Script_Armenian,   HB_SCRIPT_ARMENIAN,    0x0531},
+    {HB_Script_Hebrew,     HB_SCRIPT_HEBREW,      0x0591},
+    {HB_Script_Arabic,     HB_SCRIPT_ARABIC,      0x0600},
+    {HB_Script_Syriac,     HB_SCRIPT_SYRIAC,      0x0710},
+    {HB_Script_Thaana,     HB_SCRIPT_THAANA,      0x0780},
+    {HB_Script_Nko,        HB_SCRIPT_NKO,         0x07C0},
+    {HB_Script_Devanagari, HB_SCRIPT_DEVANAGARI,  0x0901},
+    {HB_Script_Bengali,    HB_SCRIPT_BENGALI,     0x0981},
+    {HB_Script_Gurmukhi,   HB_SCRIPT_GURMUKHI,    0x0A10},
+    {HB_Script_Gujarati,   HB_SCRIPT_GUJARATI,    0x0A90},
+    {HB_Script_Oriya,      HB_SCRIPT_ORIYA,       0x0B10},
+    {HB_Script_Tamil,      HB_SCRIPT_TAMIL,       0x0B82},
+    {HB_Script_Telugu,     HB_SCRIPT_TELUGU,      0x0C10},
+    {HB_Script_Kannada,    HB_SCRIPT_KANNADA,     0x0C90},
+    {HB_Script_Malayalam,  HB_SCRIPT_MALAYALAM,   0x0D10},
+    {HB_Script_Sinhala,    HB_SCRIPT_SINHALA,     0x0D90},
+    {HB_Script_Thai,       HB_SCRIPT_THAI,        0x0E01},
+    {HB_Script_Lao,        HB_SCRIPT_LAO,         0x0E81},
+    {HB_Script_Tibetan,    HB_SCRIPT_TIBETAN,     0x0F00},
+    {HB_Script_Myanmar,    HB_SCRIPT_MYANMAR,     0x1000},
+    {HB_Script_Georgian,   HB_SCRIPT_GEORGIAN,    0x10A0},
+    {HB_Script_Unknown,    HB_SCRIPT_ETHIOPIC,    0x1200},
+    {HB_Script_Unknown,    HB_SCRIPT_CHEROKEE,    0x13A0},
+    {HB_Script_Ogham,      HB_SCRIPT_OGHAM,       0x1680},
+    {HB_Script_Runic,      HB_SCRIPT_RUNIC,       0x16A0},
+    {HB_Script_Khmer,      HB_SCRIPT_KHMER,       0x1780},
+    {HB_Script_Unknown,    HB_SCRIPT_TAI_LE,      0x1950},
+    {HB_Script_Unknown,    HB_SCRIPT_NEW_TAI_LUE, 0x1980},
+    {HB_Script_Unknown,    HB_SCRIPT_TAI_THAM,    0x1A20},
+    {HB_Script_Unknown,    HB_SCRIPT_CHAM,        0xAA00},
+};
+
+static hb_script_t getHBScriptFromHBScriptOld(HB_Script script_old) {
+    hb_script_t script = HB_SCRIPT_INVALID;
+    int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping);
+    for (int i = 0; i < numSupportedFonts; i++) {
+        if (script_old == HB_UnicodeMappingArray[i].script_old) {
+            script = HB_UnicodeMappingArray[i].script;
+            break;
+        }
+    }
+    return script;
+}
+
+// returns 0 for "Not Found"
+static SkUnichar getUnicodeFromHBScript(hb_script_t 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_t script;
+    SkTypeface::Style style;
+    SkPaintOptionsAndroid::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;
+}
+
+SkTypeface* SkCreateTypefaceForScriptNG(hb_script_t script, SkTypeface::Style style,
+                                        SkPaintOptionsAndroid::FontVariant fontVariant) {
+    SkAutoMutexAcquire ac(gTypefaceTableMutex);
+
+    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);
+
+    SkTypeface* retTypeface = NULL;
+    if (index >= 0) {
+        retTypeface = gTypefaceTable[index].typeface;
+    }
+    else {
+        SkUnichar unichar = getUnicodeFromHBScript(script);
+        if (!unichar) {
+            return NULL;
+        }
+
+        SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface();
+        retTypeface = fontConfig->getTypefaceForChar(unichar, style, fontVariant);
+
+        // add to the lookup table
+        key.typeface = retTypeface;
+        *gTypefaceTable.insert(~index) = key;
+    }
+
+    // we ref(), the caller is expected to unref when they are done
+    return SkSafeRef(retTypeface);
+}
+
+SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style,
+                                      SkPaintOptionsAndroid::FontVariant fontVariant) {
+    return SkCreateTypefaceForScriptNG(getHBScriptFromHBScriptOld(script), style, fontVariant);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
 SkFontMgr* SkFontMgr::Factory() {
     return NULL;
 }