aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--include/core/SkLanguage.h70
-rw-r--r--include/core/SkPaint.h19
-rw-r--r--include/core/SkScalerContext.h18
-rw-r--r--src/core/SkLanguage.cpp54
-rw-r--r--src/core/SkPaint.cpp26
-rw-r--r--src/core/SkScalerContext.cpp58
-rw-r--r--src/ports/FontHostConfiguration_android.cpp75
-rw-r--r--src/ports/FontHostConfiguration_android.h18
-rw-r--r--src/ports/SkFontHost_android.cpp300
10 files changed, 407 insertions, 232 deletions
diff --git a/Android.mk b/Android.mk
index 846c157..c240d69 100644
--- a/Android.mk
+++ b/Android.mk
@@ -108,6 +108,7 @@ LOCAL_SRC_FILES:= \
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 @@ public:
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 @@ private:
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 @@ public:
uint32_t fLumBits;
#endif
#ifdef SK_BUILD_FOR_ANDROID
+ SkLanguage fLanguage;
SkPaint::FontVariant fFontVariant;
#endif
uint8_t fMaskFormat;
@@ -304,16 +309,7 @@ public:
#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 @@ protected:
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 @@ SkPaint::SkPaint() {
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 @@ SkPaint::SkPaint(const SkPaint& src) {
SkSafeRef(fRasterizer);
SkSafeRef(fLooper);
SkSafeRef(fImageFilter);
-#ifdef SK_BUILD_FOR_ANDROID
- new(&fTextLocale) SkString(src.fTextLocale);
-#endif
}
SkPaint::~SkPaint() {
@@ -130,12 +128,10 @@ SkPaint& SkPaint::operator=(const SkPaint& src) {
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 @@ void SkPaint::setTextEncoding(TextEncoding encoding) {
}
#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 @@ void SkScalerContext::MakeRec(const SkPaint& paint,
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 @@ void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const {
*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 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
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 @@ SkScalerContext* SkScalerContext::getNextContext() {
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 @@ SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
}
#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 @@ SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
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 @@ unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) {
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/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 @@ void textHandler(void *data, const char *s, int len) {
}
/**
- * 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 @@ void fontFileElementHandler(FamilyData *familyData, const char **attributes) {
} 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 @@ void endElementHandler(void *data, const char *tag) {
}
}
-#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 @@ void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
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 @@ void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) {
}
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 getFontFamilies(SkTDArray<FontFamily*> &fontFamilies);
*/
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 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;
}