diff options
-rw-r--r-- | include/core/SkFontHost.h | 9 | ||||
-rw-r--r-- | include/core/SkPaint.h | 20 | ||||
-rw-r--r-- | include/core/SkScalerContext.h | 4 | ||||
-rw-r--r-- | include/ports/SkTypeface_android.h | 45 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 16 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 8 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.cpp | 53 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.h | 17 | ||||
-rw-r--r-- | src/ports/SkFontHost_android.cpp | 266 |
9 files changed, 269 insertions, 169 deletions
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h index 25c9ecb..ace08d8 100644 --- a/include/core/SkFontHost.h +++ b/include/core/SkFontHost.h @@ -154,6 +154,15 @@ public: */ static SkFontID NextLogicalFont(SkFontID currFontID, SkFontID origFontID); +#ifdef SK_BUILD_FOR_ANDROID + /* + * This Android-only version of NextLogicalFont allows us to pass in an + * entire Rec structure so that a caller can change fallback behavior + */ + static SkFontID NextLogicalFont(const SkScalerContext::Rec& rec); +#endif + + /////////////////////////////////////////////////////////////////////////// /** Given a filled-out rec, the fonthost may decide to modify it to reflect diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 30ff663..1715013 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -665,6 +665,25 @@ public: @param locale set the paint's locale value for drawing text. */ void setTextLocale(const SkString& locale); + + + enum FontVariant { + kDefault_Variant, // Currently setting yourself to Default gives you Compact Variant + kCompact_Variant, + kElegant_Variant, + kLast_Variant = kElegant_Variant, + }; + + /** Return the font variant + @return the font variant used by this paint object + */ + FontVariant getFontVariant() const { return fFontVariant; } + + + /** Set the font variant + @param fontVariant set the paint's font variant for choosing fonts + */ + void setFontVariant(FontVariant fontVariant); #endif /** Return the paint's text size. @@ -906,6 +925,7 @@ private: unsigned fHinting : 2; #ifdef SK_BUILD_FOR_ANDROID SkString fTextLocale; + FontVariant fFontVariant; #endif SkDrawCacheProc getDrawCacheProc() const; diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index 9bcf601..33c3c3d 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -210,6 +210,9 @@ public: #ifdef SK_USE_COLOR_LUMINANCE uint32_t fLumBits; #endif +#ifdef SK_BUILD_FOR_ANDROID + SkPaint::FontVariant fFontVariant; +#endif uint8_t fMaskFormat; uint8_t fStrokeJoin; uint16_t fFlags; @@ -234,7 +237,6 @@ public: SkMask::Format getFormat() const { return static_cast<SkMask::Format>(fMaskFormat); } - #ifdef SK_USE_COLOR_LUMINANCE SkColor getLuminanceColor() const { return fLumBits; diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h index 3471a94..c3eb3d1 100644 --- a/include/ports/SkTypeface_android.h +++ b/include/ports/SkTypeface_android.h @@ -10,47 +10,20 @@ #define SkTypeface_android_DEFINED #include "SkTypeface.h" +#include "SkPaint.h" -enum FallbackScripts { - kArabic_FallbackScript, - kArmenian_FallbackScript, - kBengali_FallbackScript, - kDevanagari_FallbackScript, - kEthiopic_FallbackScript, - kGeorgian_FallbackScript, - kHebrewRegular_FallbackScript, - kHebrewBold_FallbackScript, - kKannada_FallbackScript, - kMalayalam_FallbackScript, - kTamilRegular_FallbackScript, - kTamilBold_FallbackScript, - kThai_FallbackScript, - kTelugu_FallbackScript, - kFallbackScriptNumber -}; - -#define SkTypeface_ValidScript(s) (s >= 0 && s < kFallbackScriptNumber) +#include "../harfbuzz/src/harfbuzz-shaper.h" /** * Return a new typeface for a fallback script. If the script is * not valid, or can not map to a font, returns null. - * @param script The script id. - * @return reference to the matching typeface. Caller must call - * unref() when they are done. - */ -SK_API SkTypeface* SkCreateTypefaceForScript(FallbackScripts script); - -/** - * Return the string representation for the fallback script on Android. - * If the script is not valid, returns null. - */ -SK_API const char* SkGetFallbackScriptID(FallbackScripts script); - -/** - * Return the fallback script enum for the ID on Android. - * If the ID is not valid, or can not map to a fallback - * script, returns kFallbackScriptNumber. + * @param script The harfbuzz script id. + * @param style The font style, for example bold + * @param elegant true if we want the web friendly elegant version of the font + * @return reference to the matching typeface. Caller must call + * unref() when they are done. */ -SK_API FallbackScripts SkGetFallbackScriptFromID(const char* id); +SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style, + SkPaint::FontVariant fontVariant = SkPaint::kDefault_Variant); #endif diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index e1932a7..412ab2b 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -72,6 +72,7 @@ SkPaint::SkPaint() { fHinting = SkPaintDefaults_Hinting; #ifdef SK_BUILD_FOR_ANDROID new(&fTextLocale) SkString(); + fFontVariant = kDefault_Variant; fGenerationID = 0; #endif } @@ -372,6 +373,18 @@ void SkPaint::setTextLocale(const SkString& locale) { GEN_ID_INC; } } + +void SkPaint::setFontVariant(FontVariant fontVariant) { + if ((unsigned)fontVariant <= kLast_Variant) { + GEN_ID_INC_EVAL((unsigned)fontVariant != fFontVariant); + fFontVariant = fontVariant; + } else { +#ifdef SK_REPORT_API_RANGE_CHECK + SkDebugf("SkPaint::setFontVariant(%d) out of range\n", fontVariant); +#endif + } +} + #endif /////////////////////////////////////////////////////////////////////////////// @@ -1561,6 +1574,9 @@ void SkScalerContext::MakeRec(const SkPaint& paint, #else rec->setLuminanceBits(computeLuminance(paint)); #endif +#ifdef SK_BUILD_FOR_ANDROID + rec->fFontVariant = paint.getFontVariant(); +#endif //SK_BUILD_FOR_ANDROID /* Allow the fonthost to modify our rec before we use it as a key into the cache. This way if we're asking for something that they will ignore, diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index 2921b1e..e33ad7a 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -118,7 +118,13 @@ static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) { // fonthost will determine the next possible font to search, based // on the current font in fRec. It will return NULL if ctx is our // last font that can be searched (i.e. ultimate fallback font) - uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID); +#ifdef SK_BUILD_FOR_ANDROID + // On Android, pass entire rec structure so that clients can change fallback behavior + uint32_t newFontID = SkFontHost::NextLogicalFont(rec); +#else + uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID); +#endif + if (0 == newFontID) { return NULL; } diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp index d1164c8..9296769 100644 --- a/src/ports/FontHostConfiguration_android.cpp +++ b/src/ports/FontHostConfiguration_android.cpp @@ -18,6 +18,7 @@ #include "FontHostConfiguration_android.h" #include "SkString.h" #include "SkTDArray.h" +#include "SkTypeface.h" #include <expat.h> #if !defined(SK_BUILD_FOR_ANDROID_NDK) #include <cutils/properties.h> @@ -46,12 +47,13 @@ struct FamilyData { XML_Parser *parser; // The expat parser doing the work SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed FontFamily *currentFamily; // The current family being created + FontFileInfo *currentFontInfo; // The current fontInfo being created int currentTag; // A flag to indicate whether we're in nameset/fileset tags }; /** * Handler for arbitrary text. This is used to parse the text inside each name - * or file tag. The resulting strings are put into the fNames or fFileNames arrays. + * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays. */ void textHandler(void *data, const char *s, int len) { FamilyData *familyData = (FamilyData*) data; @@ -68,7 +70,9 @@ void textHandler(void *data, const char *s, int len) { *(familyData->currentFamily->fNames.append()) = buff; break; case FILESET_TAG: - *(familyData->currentFamily->fFileNames.append()) = buff; + if (familyData->currentFontInfo) { + familyData->currentFontInfo->fFileName = buff; + } break; default: // Noop - don't care about any text that's not in the Fonts or Names list @@ -78,6 +82,39 @@ 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 + */ +void fontFileElementHandler(FamilyData *familyData, const char **attributes) { + FontFileInfo* newFileInfo = new FontFileInfo(); + if (attributes) { + int currentAttributeIndex = 0; + while (attributes[currentAttributeIndex]) { + const char* attributeName = attributes[currentAttributeIndex]; + const char* attributeValue = attributes[currentAttributeIndex+1]; + int nameLength = strlen(attributeName); + int valueLength = strlen(attributeValue); + if (strncmp(attributeName, "variant", nameLength) == 0) { + if (strncmp(attributeValue, "elegant", valueLength) == 0) { + newFileInfo->fVariant = SkPaint::kElegant_Variant; + } 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) + } + //each element is a pair of attributeName/attributeValue string pairs + currentAttributeIndex += 2; + } + } + *(familyData->currentFamily->fFontFileArray.append()) = newFileInfo; + familyData->currentFontInfo = newFileInfo; + XML_SetCharacterDataHandler(*familyData->parser, textHandler); +} + +/** * Handler for the start of a tag. The only tags we expect are family, nameset, * fileset, name, and file. */ @@ -98,14 +135,16 @@ void startElementHandler(void *data, const char *tag, const char **atts) { familyData->currentFamily->order = value; } } - } else if (len == 7 && strncmp(tag, "nameset", len)== 0) { + } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { familyData->currentTag = NAMESET_TAG; } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { familyData->currentTag = FILESET_TAG; - } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || - (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { + } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) { // If it's a Name, parse the text inside XML_SetCharacterDataHandler(*familyData->parser, textHandler); + } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) { + // If it's a file, parse the attributes, then parse the text inside + fontFileElementHandler(familyData, atts); } } @@ -120,9 +159,9 @@ void endElementHandler(void *data, const char *tag) { // Done parsing a Family - store the created currentFamily in the families array *familyData->families.append() = familyData->currentFamily; familyData->currentFamily = NULL; - } else if (len == 7 && strncmp(tag, "nameset", len)== 0) { + } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { familyData->currentTag = NO_TAG; - } else if (len == 7 && strncmp(tag, "fileset", len)== 0) { + } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { familyData->currentTag = NO_TAG; } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h index 2441f0e..c8a70e3 100644 --- a/src/ports/FontHostConfiguration_android.h +++ b/src/ports/FontHostConfiguration_android.h @@ -18,18 +18,27 @@ #define FONTHOSTCONFIGURATION_ANDROID_H_ #include "SkTDArray.h" +#include "SkPaint.h" + +struct FontFileInfo { + FontFileInfo() : fVariant(SkPaint::kDefault_Variant), fLanguage(NULL), fFileName(NULL) {} + const char* fFileName; + SkPaint::FontVariant fVariant; + const char* fLanguage; // We may eventually use a enum for this +}; + /** * 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 - * font names that alias to a font family. fFileNames is the list of font - * filenames for the family. Order is the priority order for the font. This is + * font names that alias to a font family. fontFileArray is the list of information + * about each file. Order is the priority order for the font. This is * used internally to determine the order in which to place fallback fonts as * they are read from the configuration files. */ struct FontFamily { - SkTDArray<const char*> fNames; - SkTDArray<const char*> fFileNames; + SkTDArray<const char*> fNames; + SkTDArray<FontFileInfo*> fFontFileArray; int order; }; diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index afd0ebe..0a9cced 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -29,7 +29,7 @@ #include <string.h> #include "SkGlyphCache.h" #include "SkTypeface_android.h" - +#include "SkTSearch.h" //#define SkDEBUGF(args ) SkDebugf args @@ -76,7 +76,7 @@ static SkTypeface* createTypefaceLocked(const SkTypeface* familyFace, SkTypeface::Style style); static SkStream* openStreamLocked(uint32_t fontID); static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int32_t* index); -static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID); +static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec); static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream); /////////////////////////////////////////////////////////////////////////////// @@ -150,6 +150,12 @@ static SkTypeface* findBestFaceLocked(const FamilyRec* family, return NULL; } +static SkTypeface* FindBestFace(const FamilyRec* family, + SkTypeface::Style style) { + SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex); + return findBestFaceLocked(family, style); +} + static FamilyRec* findFamilyLocked(const SkTypeface* member) { FamilyRec* curr = gFamilyHead; while (curr != NULL) { @@ -180,6 +186,14 @@ static SkTypeface* findFromUniqueIDLocked(uint32_t uniqueID) { return NULL; } +/* Returns the matching typeface, or NULL. If a typeface is found, its refcnt + is not modified. + */ +static SkTypeface* FindFromUniqueID(uint32_t uniqueID) { + SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex); + return findFromUniqueIDLocked(uniqueID); +} + /* Remove reference to this face from its family. If the resulting family is empty (has no faces), return that family, otherwise return NULL */ @@ -397,8 +411,15 @@ private: // used to record our notion of the pre-existing fonts struct FontInitRec { - const char* fFileName; - const char* const* fNames; // null-terminated list + const char* fFileName; + const char* const* fNames; // null-terminated list + SkPaint::FontVariant fVariant; +}; + +//used to record information about the fallback fonts +struct FallbackFontRec { + SkFontID fFontID; + SkPaint::FontVariant fVariant; }; // deliberately empty, but we use the address to identify fallback fonts @@ -410,7 +431,7 @@ static const char* gFBNames[] = { NULL }; null for the list. The names list must be NULL-terminated. */ static SkTDArray<FontInitRec> gSystemFonts; -static SkTDArray<SkFontID> gFallbackFonts; +static SkTDArray<FallbackFontRec> gFallbackFonts; // these globals are assigned (once) by loadSystemFontsLocked() static FamilyRec* gDefaultFamily = NULL; @@ -493,8 +514,8 @@ static void loadFontInfoLocked() { for (int i = 0; i < fontFamilies.count(); ++i) { FontFamily *family = fontFamilies[i]; - for (int j = 0; j < family->fFileNames.count(); ++j) { - const char* filename = family->fFileNames[j]; + for (int j = 0; j < family->fFontFileArray.count(); ++j) { + const char* filename = family->fFontFileArray[j]->fFileName; if (haveSystemFont(filename)) { SkDebugf("---- system font and fallback font files specify a duplicate " "font %s, skipping the second occurrence", filename); @@ -503,6 +524,7 @@ static void loadFontInfoLocked() { FontInitRec fontInfoRecord; fontInfoRecord.fFileName = filename; + fontInfoRecord.fVariant = family->fFontFileArray[j]->fVariant; if (j == 0) { if (family->fNames.count() == 0) { // Fallback font @@ -595,7 +617,10 @@ static void initSystemFontsLocked() { if (names == gFBNames) { SkDEBUGF(("---- adding %s as fallback[%d] fontID %d\n", gSystemFonts[i].fFileName, gFallbackFonts.count(), tf->uniqueID())); - *gFallbackFonts.append() = tf->uniqueID(); + FallbackFontRec newFallbackRec; + newFallbackRec.fFontID = tf->uniqueID(); + newFallbackRec.fVariant = gSystemFonts[i].fVariant; + *gFallbackFonts.append() = newFallbackRec; } firstInFamily = tf; @@ -634,7 +659,7 @@ static SkFontID findUniqueIDLocked(const char* filename) { static int findFallbackFontIndex(SkFontID fontId) { for (int i = 0; i < gFallbackFonts.count(); i++) { - if (gFallbackFonts[i] == fontId) { + if (gFallbackFonts[i].fFontID == fontId) { return i; } } @@ -652,8 +677,8 @@ static void reloadFallbackFontsLocked() { for (int i = 0; i < fallbackFamilies.count(); ++i) { FontFamily *family = fallbackFamilies[i]; - for (int j = 0; j < family->fFileNames.count(); ++j) { - const char* filename = family->fFileNames[j]; + 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 " @@ -680,8 +705,10 @@ static void reloadFallbackFontsLocked() { SkDEBUGF(("---- reload %s as fallback[%d] fontID %d\n", filename, gFallbackFonts.count(), uniqueID)); - - *gFallbackFonts.append() = 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 } } @@ -886,16 +913,16 @@ static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int } } -SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { +SkFontID SkFontHost::NextLogicalFont(const SkScalerContext::Rec& rec) { SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex); - return nextLogicalFontLocked(currFontID, origFontID); + return nextLogicalFontLocked(rec); } -static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID) { +static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) { loadSystemFontsLocked(); - const SkTypeface* origTypeface = findFromUniqueIDLocked(origFontID); - const SkTypeface* currTypeface = findFromUniqueIDLocked(currFontID); + const SkTypeface* origTypeface = findFromUniqueIDLocked(rec.fOrigFontID); + const SkTypeface* currTypeface = findFromUniqueIDLocked(rec.fFontID); SkASSERT(origTypeface != 0); SkASSERT(currTypeface != 0); @@ -911,17 +938,30 @@ static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID) */ int plainFallbackFontIndex = findFallbackFontIndex(plainFontID); int nextFallbackFontIndex = plainFallbackFontIndex + 1; - SkFontID nextFontID; - if (nextFallbackFontIndex == gFallbackFonts.count()) { - nextFontID = 0; // no more fallbacks - } else { - const SkTypeface* nextTypeface = findFromUniqueIDLocked(gFallbackFonts[nextFallbackFontIndex]); - nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID(); + + // If a rec object is set to prefer "kDefault_Variant" it means they have no preference + // In this case, we set the value to "kCompact_Variant" + SkPaint::FontVariant recPreference = rec.fFontVariant; + if (recPreference == SkPaint::kDefault_Variant) { + recPreference = SkPaint::kCompact_Variant; + } + SkFontID nextFontID = 0; + while (nextFallbackFontIndex < gFallbackFonts.count()) { + bool normalFont = + (gFallbackFonts[nextFallbackFontIndex].fVariant == SkPaint::kDefault_Variant); + bool fontChosen = (gFallbackFonts[nextFallbackFontIndex].fVariant == recPreference); + if (normalFont || fontChosen) { + const SkTypeface* nextTypeface = + findFromUniqueIDLocked(gFallbackFonts[nextFallbackFontIndex].fFontID); + nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID(); + break; + } + nextFallbackFontIndex++; } SkDEBUGF(("---- nextLogicalFont: currFontID=%d, origFontID=%d, plainFontID=%d, " "plainFallbackFontIndex=%d, nextFallbackFontIndex=%d " - "=> nextFontID=%d", currFontID, origFontID, plainFontID, + "=> nextFontID=%d", rec.fFontID, rec.fOrigFontID, plainFontID, plainFallbackFontIndex, nextFallbackFontIndex, nextFontID)); return nextFontID; } @@ -966,46 +1006,9 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { // Function from SkTypeface_android.h /////////////////////////////////////////////////////////////////////////////// -struct FBScriptInfo { - const FallbackScripts fScript; - const char* fScriptID; - const SkTypeface::Style fStyle; - const SkUnichar fChar; // representative character for that script type - SkFontID fFontID; -}; - -#define SK_DEFINE_SCRIPT_ENTRY(script, style, unichar) \ - { script, #script, style, unichar, 0 } - -static FBScriptInfo gFBScriptInfo[] = { - SK_DEFINE_SCRIPT_ENTRY(kArabic_FallbackScript, SkTypeface::kNormal, 0x0600), - SK_DEFINE_SCRIPT_ENTRY(kArmenian_FallbackScript, SkTypeface::kNormal, 0x0531), - SK_DEFINE_SCRIPT_ENTRY(kBengali_FallbackScript, SkTypeface::kNormal, 0x0981), - SK_DEFINE_SCRIPT_ENTRY(kDevanagari_FallbackScript, SkTypeface::kNormal, 0x0901), - SK_DEFINE_SCRIPT_ENTRY(kEthiopic_FallbackScript, SkTypeface::kNormal, 0x1200), - SK_DEFINE_SCRIPT_ENTRY(kGeorgian_FallbackScript, SkTypeface::kNormal, 0x10A0), - SK_DEFINE_SCRIPT_ENTRY(kHebrewRegular_FallbackScript, SkTypeface::kNormal, 0x0591), - SK_DEFINE_SCRIPT_ENTRY(kHebrewBold_FallbackScript, SkTypeface::kBold, 0x0591), - SK_DEFINE_SCRIPT_ENTRY(kKannada_FallbackScript, SkTypeface::kNormal, 0x0C90), - SK_DEFINE_SCRIPT_ENTRY(kMalayalam_FallbackScript, SkTypeface::kNormal, 0x0D10), - SK_DEFINE_SCRIPT_ENTRY(kTamilRegular_FallbackScript, SkTypeface::kNormal, 0x0B82), - SK_DEFINE_SCRIPT_ENTRY(kTamilBold_FallbackScript, SkTypeface::kBold, 0x0B82), - SK_DEFINE_SCRIPT_ENTRY(kThai_FallbackScript, SkTypeface::kNormal, 0x0E01), - SK_DEFINE_SCRIPT_ENTRY(kTelugu_FallbackScript, SkTypeface::kNormal, 0x0C10), -}; - -static bool gFBScriptInitialized = false; -static const int gFBScriptInfoCount = sizeof(gFBScriptInfo) / sizeof(FBScriptInfo); - -// ensure that if any value is added to the public enum it is also added here -SK_COMPILE_ASSERT(gFBScriptInfoCount == kFallbackScriptNumber, FBScript_count_mismatch); - - -// this function can't be called if the gFamilyHeadAndNameListMutex is already locked -static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style) { - gFamilyHeadAndNameListMutex.acquire(); - SkTypeface* face = findBestFaceLocked(gDefaultFamily, style); - gFamilyHeadAndNameListMutex.release(); +static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style, + SkPaint::FontVariant fontVariant) { + SkTypeface* face = FindBestFace(gDefaultFamily, style); if (!face) { return 0; } @@ -1013,6 +1016,7 @@ static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style) { SkPaint paint; paint.setTypeface(face); paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + paint.setFontVariant(fontVariant); SkAutoGlyphCache autoCache(paint, NULL); SkGlyphCache* cache = autoCache.getCache(); @@ -1025,71 +1029,93 @@ static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style) { return 0; } -// this function can't be called if the gFamilyHeadAndNameListMutex is already locked -static void initFBScriptInfo() { - if (gFBScriptInitialized) { - return; - } +struct HB_UnicodeMapping { + HB_Script script; + const SkUnichar unicode; +}; - // ensure the system fonts are loaded - gFamilyHeadAndNameListMutex.acquire(); - loadSystemFontsLocked(); - gFamilyHeadAndNameListMutex.release(); - - for (int i = 0; i < gFBScriptInfoCount; i++) { - FBScriptInfo& scriptInfo = gFBScriptInfo[i]; - // selects the best available style for the desired font. However, if - // bold is requested and no bold font exists for the typeface containing - // the character the next best style is chosen (e.g. normal). - scriptInfo.fFontID = findFontIDForChar(scriptInfo.fChar, scriptInfo.fStyle); - SkDEBUGF(("gFBScriptInfo[%s] --> %d", scriptInfo.fScriptID, scriptInfo.fFontID)); - } - // mark the value as initialized so we don't repeat our work unnecessarily - gFBScriptInitialized = true; -} +static HB_UnicodeMapping HB_UnicodeMappingArray[] { + {HB_Script_Arabic, 0x0600}, + {HB_Script_Armenian, 0x0531}, + {HB_Script_Bengali, 0x0981}, + {HB_Script_Devanagari, 0x0901}, + // we don't currently support HB_Script_Ethiopic, it is a placeholder for an upstream merge + //{HB_Script_Ethiopic, 0x1200}, + {HB_Script_Georgian, 0x10A0}, + {HB_Script_Hebrew, 0x0591}, + {HB_Script_Kannada, 0x0C90}, + {HB_Script_Malayalam, 0x0D10}, + {HB_Script_Tamil, 0x0B82}, + {HB_Script_Thai, 0x0E01}, + {HB_Script_Telugu, 0x0C10}, +}; -SkTypeface* SkCreateTypefaceForScript(FallbackScripts script) { - if (!SkTypeface_ValidScript(script)) { - return NULL; +// returns 0 for "Not Found" +static SkUnichar getUnicodeFromHBScript(HB_Script script) { + SkUnichar unichar = 0; + int numSupportedFonts = sizeof(HB_UnicodeMappingArray) / sizeof(HB_UnicodeMapping); + for (int i = 0; i < numSupportedFonts; i++) { + if (script == HB_UnicodeMappingArray[i].script) { + unichar = HB_UnicodeMappingArray[i].unicode; + break; + } } + return unichar; +} - // ensure that our table is populated - initFBScriptInfo(); - - FBScriptInfo& scriptInfo = gFBScriptInfo[script]; +struct TypefaceLookupStruct { + HB_Script script; + SkTypeface::Style style; + SkPaint::FontVariant fontVariant; + SkTypeface* typeface; +}; - // ensure the element with that index actually maps to the correct script - SkASSERT(scriptInfo.fScript == script); +SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable +static SkTDArray<TypefaceLookupStruct> gTypefaceTable; // This is protected by gTypefaceTableMutex - // if a suitable script could not be found then return NULL - if (scriptInfo.fFontID == 0) { - return NULL; +static int typefaceLookupCompare(const TypefaceLookupStruct& first, + const TypefaceLookupStruct& second) { + if (first.script != second.script) { + return (first.script > second.script) ? 1 : -1; } - - SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex); - - // retrieve the typeface the corresponds to this fontID - SkTypeface* tf = findFromUniqueIDLocked(scriptInfo.fFontID); - // we ref(), since the semantic is to return a new instance - tf->ref(); - return tf; -} - -const char* SkGetFallbackScriptID(FallbackScripts script) { - for (int i = 0; i < gFBScriptInfoCount; i++) { - if (gFBScriptInfo[i].fScript == script) { - return gFBScriptInfo[i].fScriptID; - } + if (first.style != second.style) { + return (first.style > second.style) ? 1 : -1; } - return NULL; + if (first.fontVariant != second.fontVariant) { + return (first.fontVariant > second.fontVariant) ? 1 : -1; + } + return 0; } -FallbackScripts SkGetFallbackScriptFromID(const char* id) { - for (int i = 0; i < gFBScriptInfoCount; i++) { - if (strcmp(gFBScriptInfo[i].fScriptID, id) == 0) { - return gFBScriptInfo[i].fScript; +SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style, + SkPaint::FontVariant fontVariant) { + SkTypeface* retTypeface = NULL; + + SkAutoMutexAcquire ac(gTypefaceTableMutex); // Note: NOT gFamilyHeadAndNameListMutex + TypefaceLookupStruct key; + key.script = script; + key.style = style; + key.fontVariant = fontVariant; + int index = SkTSearch<TypefaceLookupStruct>( + (const TypefaceLookupStruct*) gTypefaceTable.begin(), + gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct), + &typefaceLookupCompare); + if (index >= 0) { + retTypeface = gTypefaceTable[index].typeface; + } + else { + SkUnichar unichar = getUnicodeFromHBScript(script); + if (!unichar) { + return NULL; } + SkFontID newFontID = findFontIDForChar(unichar, style, fontVariant); + // retrieve the typeface that corresponds to this fontID + retTypeface = FindFromUniqueID(newFontID); + // we ref(), since the semantic is to return a new instance + retTypeface->ref(); + key.typeface = retTypeface; + index = ~index; + *gTypefaceTable.insert(index) = key; } - return kFallbackScriptNumber; // Use kFallbackScriptNumber as an invalid value. + return retTypeface; } - |