aboutsummaryrefslogtreecommitdiffstats
path: root/src/ports/SkFontHost_android.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ports/SkFontHost_android.cpp')
-rw-r--r--src/ports/SkFontHost_android.cpp499
1 files changed, 385 insertions, 114 deletions
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 @@ static SkTypeface* createTypefaceLocked(const SkTypeface* familyFace,
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 @@ static SkTypeface* findBestFaceLocked(const FamilyRec* family,
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 @@ static SkTypeface* findFromUniqueIDLocked(uint32_t uniqueID) {
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 @@ private:
// 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 @@ static void dumpGlobalsLocked() {
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 @@ static bool haveSystemFont(const char* filename) {
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 @@ static void loadFontInfoLocked() {
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 @@ static void loadFontInfoLocked() {
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 @@ static void loadFontInfoLocked() {
} else {
fontInfoRecord.fNames = NULL;
}
- *gSystemFonts.append() = fontInfoRecord;
+ gSystemFonts.push_back(fontInfoRecord);
}
}
fontFamilies.deleteAll();
@@ -535,7 +730,6 @@ static void loadFontInfoLocked() {
}
}
-
/*
* 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 @@ static void initSystemFontsLocked() {
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 @@ static void initSystemFontsLocked() {
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 @@ static void initSystemFontsLocked() {
}
}
}
+ 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 @@ static void initSystemFontsLocked() {
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 @@ static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int
}
}
-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 @@ static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID)
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 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
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;
+}