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.cpp300
1 files changed, 194 insertions, 106 deletions
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 size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int
static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec);
static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream);
+
///////////////////////////////////////////////////////////////////////////////
struct FamilyRec;
@@ -414,6 +418,7 @@ struct FontInitRec {
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 @@ struct FallbackFontRec {
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 @@ 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);
@@ -504,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);
@@ -525,6 +694,7 @@ 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
@@ -549,7 +719,7 @@ static void loadFontInfoLocked() {
} else {
fontInfoRecord.fNames = NULL;
}
- *gSystemFonts.append() = fontInfoRecord;
+ gSystemFonts.push_back(fontInfoRecord);
}
}
fontFamilies.deleteAll();
@@ -560,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.
@@ -575,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
@@ -615,12 +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()));
- 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 @@ 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
@@ -645,102 +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].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 @@ static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) {
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 @@ static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) {
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 @@ static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) {
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;
}