diff options
author | Billy Hewlett <billyh@google.com> | 2012-07-03 17:03:55 -0700 |
---|---|---|
committer | Victoria Lease <violets@google.com> | 2012-08-20 08:43:58 -0700 |
commit | 725b3e03acb91dc69296554932624b36612bd189 (patch) | |
tree | 56edf31f828b49d7b202522b91ac389a29e55714 | |
parent | 8ab6679ce80dafa0d97c426c9fe70c6b6ceb20e6 (diff) | |
download | external_skia-725b3e03acb91dc69296554932624b36612bd189.zip external_skia-725b3e03acb91dc69296554932624b36612bd189.tar.gz external_skia-725b3e03acb91dc69296554932624b36612bd189.tar.bz2 |
DO NOT MERGE Han Preference
Cherry-pick Id8c91ae0be6cad8a7ef77a0cd5803676290986c1 from master.
During font initialization, create a seperate fallback list for each
locale. At runtime, use the fallbacklist associated with the locale
set in the paint object. Fallback files are associated with locales in
fallback_fonts.xml. Multiple files can be associated with the same
locale, ordering within that langauge and apart from that language in
the fallback order is preserved.
This changelist also includes some refactoring, notably of the
functions that call getNextContext().
Change-Id: I121f0e491a522c4a8558a0066b2d8969fb8a3667
-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; } |