Pass the whole locale list down to Minikin.

Also, compute LocaleList's string representation at construction.
This is to further push the cost of doing costly operations related
to LocaleLists to construction time.

Change-Id: Ia55b8ce66b1088ff54cb42eb1e11149b5bd10f17
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index d1780d6..7fd288a 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -33,11 +33,11 @@
     FontStyle resolved = resolvedFace->fStyle;
 
     /* Prepare minikin FontStyle */
-    const std::string& lang = paint->getTextLocale();
-    FontLanguage minikinLang(lang.c_str(), lang.size());
+    const std::string& langs = paint->getTextLocales();
+    FontLanguages minikinLangs(langs.c_str(), langs.size());
     FontVariant minikinVariant = (paint->getFontVariant() == VARIANT_ELEGANT) ? VARIANT_ELEGANT
             : VARIANT_COMPACT;
-    FontStyle minikinStyle(minikinLang, minikinVariant, resolved.getWeight(), resolved.getItalic());
+    FontStyle minikinStyle(minikinLangs, minikinVariant, resolved.getWeight(), resolved.getItalic());
 
     /* Prepare minikin Paint */
     // Note: it would be nice to handle fractional size values (it would improve smooth zoom
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 314e4b6..6b09450 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -43,12 +43,10 @@
 #include "Paint.h"
 #include "TypefaceImpl.h"
 
-#include <vector>
+#include <cassert>
+#include <cstring>
 #include <memory>
-
-// temporary for debugging
-#include <Caches.h>
-#include <utils/Log.h>
+#include <vector>
 
 namespace android {
 
@@ -71,12 +69,12 @@
     paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
 }
 
-struct LocaleCacheEntry {
-    std::string javaLocale;
-    std::string languageTag;
+struct LocalesCacheEntry {
+    std::string javaLocales;
+    std::string languageTags;
 };
 
-static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
+static thread_local LocalesCacheEntry sSingleEntryLocalesCache;
 
 namespace PaintGlue {
     enum MoveOpt {
@@ -360,17 +358,57 @@
         output[0] = '\0';
     }
 
-    static void setTextLocale(JNIEnv* env, jobject clazz, jlong objHandle, jstring locale) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        ScopedUtfChars localeChars(env, locale);
-        if (sSingleEntryLocaleCache.javaLocale != localeChars.c_str()) {
-            sSingleEntryLocaleCache.javaLocale = localeChars.c_str();
-            char langTag[ULOC_FULLNAME_CAPACITY];
-            toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
-            sSingleEntryLocaleCache.languageTag = langTag;
+    static void toLanguageTags(std::string* output, const char* locales) {
+        if (output == NULL) {
+            return;
+        }
+        if (locales == NULL) {
+            output->clear();
+            return;
         }
 
-        obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
+        char langTag[ULOC_FULLNAME_CAPACITY];
+        const char* commaLoc = strchr(locales, ',');
+        if (commaLoc == NULL) {
+            assert(locales[0] != '\0');  // the string should not be empty
+            toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locales);
+            *output = langTag;
+            return;
+        }
+
+        size_t len = strlen(locales);
+        char locale[len];
+        output->clear();
+        output->reserve(len);
+        const char* lastStart = locales;
+        do {
+            assert(lastStart > commaLoc);  // the substring should not be empty
+            strncpy(locale, lastStart, commaLoc - lastStart);
+            locale[commaLoc - lastStart] = '\0';
+            toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
+            if (langTag[0] != '\0') {
+                output->append(langTag);
+                output->push_back(',');
+            }
+            lastStart = commaLoc + 1;
+            commaLoc = strchr(lastStart, ',');
+        } while (commaLoc != NULL);
+        assert(lastStart[0] != '\0');  // the final substring should not be empty
+        toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, lastStart);
+        if (langTag[0] != '\0') {
+            output->append(langTag);
+        }
+    }
+
+    static void setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        ScopedUtfChars localesChars(env, locales);
+        if (sSingleEntryLocalesCache.javaLocales != localesChars.c_str()) {
+            sSingleEntryLocalesCache.javaLocales = localesChars.c_str();
+            toLanguageTags(&sSingleEntryLocalesCache.languageTags, localesChars.c_str());
+        }
+
+        obj->setTextLocales(sSingleEntryLocalesCache.languageTags);
     }
 
     static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
@@ -952,7 +990,7 @@
     {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
     {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
     {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
-    {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+    {"nSetTextLocales","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocales},
     {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
     {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
     {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
diff --git a/core/jni/android/graphics/Paint.h b/core/jni/android/graphics/Paint.h
index 6df22ff..7a34bc2 100644
--- a/core/jni/android/graphics/Paint.h
+++ b/core/jni/android/graphics/Paint.h
@@ -53,12 +53,12 @@
         return mFontFeatureSettings;
     }
 
-    void setTextLocale(const std::string &textLocale) {
-        mTextLocale = textLocale;
+    void setTextLocales(const std::string &textLocales) {
+        mTextLocales = textLocales;
     }
 
-    const std::string& getTextLocale() const {
-        return mTextLocale;
+    const std::string& getTextLocales() const {
+        return mTextLocales;
     }
 
     void setFontVariant(FontVariant variant) {
@@ -80,7 +80,7 @@
 private:
     float mLetterSpacing = 0;
     std::string mFontFeatureSettings;
-    std::string mTextLocale;
+    std::string mTextLocales;
     FontVariant mFontVariant;
     uint32_t mHyphenEdit = 0;
 };
diff --git a/core/jni/android/graphics/PaintImpl.cpp b/core/jni/android/graphics/PaintImpl.cpp
index da85018..d5a0972 100644
--- a/core/jni/android/graphics/PaintImpl.cpp
+++ b/core/jni/android/graphics/PaintImpl.cpp
@@ -23,12 +23,12 @@
 namespace android {
 
 Paint::Paint() : SkPaint(),
-        mLetterSpacing(0), mFontFeatureSettings(), mTextLocale(), mFontVariant(VARIANT_DEFAULT) {
+        mLetterSpacing(0), mFontFeatureSettings(), mTextLocales(), mFontVariant(VARIANT_DEFAULT) {
 }
 
 Paint::Paint(const Paint& paint) : SkPaint(paint),
         mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings),
-        mTextLocale(paint.mTextLocale), mFontVariant(paint.mFontVariant),
+        mTextLocales(paint.mTextLocales), mFontVariant(paint.mFontVariant),
         mHyphenEdit(paint.mHyphenEdit) {
 }
 
@@ -39,7 +39,7 @@
     SkPaint::operator=(other);
     mLetterSpacing = other.mLetterSpacing;
     mFontFeatureSettings = other.mFontFeatureSettings;
-    mTextLocale = other.mTextLocale;
+    mTextLocales = other.mTextLocales;
     mFontVariant = other.mFontVariant;
     mHyphenEdit = other.mHyphenEdit;
     return *this;
@@ -49,7 +49,7 @@
     return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b)
             && a.mLetterSpacing == b.mLetterSpacing
             && a.mFontFeatureSettings == b.mFontFeatureSettings
-            && a.mTextLocale == b.mTextLocale
+            && a.mTextLocales == b.mTextLocales
             && a.mFontVariant == b.mFontVariant
             && a.mHyphenEdit == b.mHyphenEdit;
 }