diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | include/core/SkLanguage.h | 70 | ||||
-rw-r--r-- | include/core/SkPaint.h | 19 | ||||
-rw-r--r-- | include/core/SkScalerContext.h | 18 | ||||
-rw-r--r-- | src/core/SkLanguage.cpp | 54 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 26 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 58 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.cpp | 75 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.h | 18 | ||||
-rw-r--r-- | src/ports/SkFontHost_android.cpp | 300 |
10 files changed, 407 insertions, 232 deletions
@@ -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; } |