From cde7dcc5111806870f94721561056e990a9211c8 Mon Sep 17 00:00:00 2001 From: Billy Hewlett Date: Mon, 4 Jun 2012 13:37:11 -0700 Subject: Fix hardcoded font path. Allow adding new font path thru Skia changes. Bug: 6609231 Change-Id: Ie37ada42e7e78ab78318f3ed76eb627bffaddb1a --- src/core/SkGlyphCache.h | 2 + src/ports/SkFontHost_android.cpp | 135 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) (limited to 'src') diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h index 2895c54..23f3c55 100644 --- a/src/core/SkGlyphCache.h +++ b/src/core/SkGlyphCache.h @@ -123,6 +123,8 @@ public: // call the proc) void removeAuxProc(void (*auxProc)(void*)); + SkScalerContext* getScalerContext() const { return fScalerContext; } + /** Call proc on all cache entries, stopping early if proc returns true. The proc should not create or delete caches, since it could produce deadlock. diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 2c58079..afd0ebe 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -27,6 +27,9 @@ #include "FontHostConfiguration_android.h" #include #include +#include "SkGlyphCache.h" +#include "SkTypeface_android.h" + //#define SkDEBUGF(args ) SkDebugf args @@ -958,3 +961,135 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { stream->unref(); return face; } + +/////////////////////////////////////////////////////////////////////////////// +// 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(); + if (!face) { + return 0; + } + + SkPaint paint; + paint.setTypeface(face); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + SkAutoGlyphCache autoCache(paint, NULL); + SkGlyphCache* cache = autoCache.getCache(); + SkFontID fontID = 0; + + SkScalerContext* ctx = cache->getScalerContext(); + if (ctx) { + return ctx->findTypefaceIdForChar(uni); + } + return 0; +} + +// this function can't be called if the gFamilyHeadAndNameListMutex is already locked +static void initFBScriptInfo() { + if (gFBScriptInitialized) { + return; + } + + // 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; +} + +SkTypeface* SkCreateTypefaceForScript(FallbackScripts script) { + if (!SkTypeface_ValidScript(script)) { + return NULL; + } + + // ensure that our table is populated + initFBScriptInfo(); + + FBScriptInfo& scriptInfo = gFBScriptInfo[script]; + + // ensure the element with that index actually maps to the correct script + SkASSERT(scriptInfo.fScript == script); + + // if a suitable script could not be found then return NULL + if (scriptInfo.fFontID == 0) { + return NULL; + } + + 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; + } + } + return NULL; +} + +FallbackScripts SkGetFallbackScriptFromID(const char* id) { + for (int i = 0; i < gFBScriptInfoCount; i++) { + if (strcmp(gFBScriptInfo[i].fScriptID, id) == 0) { + return gFBScriptInfo[i].fScript; + } + } + return kFallbackScriptNumber; // Use kFallbackScriptNumber as an invalid value. +} + -- cgit v1.1 From 22c3eb1bb4ab1c58edb7b3dd159e5c1969136c51 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Thu, 31 May 2012 11:29:48 +0530 Subject: Prepare Skia-Webp decoder for Alpha and lossless. Change the webp_parse_header to read appropriate header bytes (required for Alpha & lossless bit-stream); Replaced call WebPGetInfo with WebPGetFeatures to get hasAlpha information; Replaced hard-coded setIsOpaque(true) with setIsOpaque(!hasAlpha); Refactored code for setting decoder config; Change-Id: I8208233d1aaa0a213a35dd996a72e43f78901c89 --- src/images/SkImageDecoder_libwebp.cpp | 100 +++++++++++++++------------------- 1 file changed, 45 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp index 3e416cc..b2aea24 100644 --- a/src/images/SkImageDecoder_libwebp.cpp +++ b/src/images/SkImageDecoder_libwebp.cpp @@ -56,20 +56,29 @@ static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap"; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -static const size_t WEBP_VP8_HEADER_SIZE = 30; +static const size_t WEBP_VP8_HEADER_SIZE = 64; static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); // Parse headers of RIFF container, and check for valid Webp (VP8) content. -static bool webp_parse_header(SkStream* stream, int* width, int* height) { +static bool webp_parse_header(SkStream* stream, int* width, int* height, + int* alpha) { unsigned char buffer[WEBP_VP8_HEADER_SIZE]; + const uint32_t contentSize = stream->getLength(); const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE); - if (len != WEBP_VP8_HEADER_SIZE) { + const uint32_t read_bytes = (contentSize < WEBP_VP8_HEADER_SIZE) ? + contentSize : WEBP_VP8_HEADER_SIZE; + if (len != read_bytes) { return false; // can't read enough } - if (WebPGetInfo(buffer, WEBP_VP8_HEADER_SIZE, width, height) == 0) { + WebPBitstreamFeatures features; + VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features); + if (status != VP8_STATUS_OK) { return false; // Invalid WebP file. } + *width = features.width; + *height = features.height; + *alpha = features.has_alpha; // sanity check for image size that's about to be decoded. { @@ -102,6 +111,7 @@ private: SkStream *inputStream; int origWidth; int origHeight; + int hasAlpha; }; ////////////////////////////////////////////////////////////////////////// @@ -160,10 +170,8 @@ static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) { stream->rewind(); const uint32_t contentSize = stream->getLength(); - uint32_t read_buffer_size = contentSize; - if (read_buffer_size > WEBP_IDECODE_BUFFER_SZ) { - read_buffer_size = WEBP_IDECODE_BUFFER_SZ; - } + const uint32_t read_buffer_size = (contentSize < WEBP_IDECODE_BUFFER_SZ) ? + contentSize : WEBP_IDECODE_BUFFER_SZ; SkAutoMalloc srcStorage(read_buffer_size); unsigned char* input = (uint8_t*)srcStorage.get(); if (input == NULL) { @@ -175,8 +183,8 @@ static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) { uint32_t bytes_remaining = contentSize; while (bytes_remaining > 0) { const uint32_t bytes_to_read = - (bytes_remaining > WEBP_IDECODE_BUFFER_SZ) ? - WEBP_IDECODE_BUFFER_SZ : bytes_remaining; + (bytes_remaining < WEBP_IDECODE_BUFFER_SZ) ? + bytes_remaining : WEBP_IDECODE_BUFFER_SZ; const size_t bytes_read = stream->read(input, bytes_to_read); if (bytes_read == 0) { @@ -201,9 +209,9 @@ static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) { } } -static bool webp_get_config_resize_crop(WebPDecoderConfig& config, - SkBitmap* decodedBitmap, - SkIRect region) { +static bool webp_get_config_resize(WebPDecoderConfig& config, + SkBitmap* decodedBitmap, + int width, int height) { WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap); if (mode == MODE_LAST) { return false; @@ -219,14 +227,8 @@ static bool webp_get_config_resize_crop(WebPDecoderConfig& config, config.output.u.RGBA.size = decodedBitmap->getSize(); config.output.is_external_memory = 1; - config.options.use_cropping = 1; - config.options.crop_left = region.fLeft; - config.options.crop_top = region.fTop; - config.options.crop_width = region.width(); - config.options.crop_height = region.height(); - - if (region.width() != decodedBitmap->width() || - region.height() != decodedBitmap->height()) { + if (width != decodedBitmap->width() || + height != decodedBitmap->height()) { config.options.use_scaling = 1; config.options.scaled_width = decodedBitmap->width(); config.options.scaled_height = decodedBitmap->height(); @@ -235,37 +237,24 @@ static bool webp_get_config_resize_crop(WebPDecoderConfig& config, return true; } -static bool webp_get_config_resize(WebPDecoderConfig& config, - SkBitmap* decodedBitmap, int origWidth, - int origHeight) { - WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap); - if (mode == MODE_LAST) { - return false; - } - - if (WebPInitDecoderConfig(&config) == 0) { - return false; - } +static bool webp_get_config_resize_crop(WebPDecoderConfig& config, + SkBitmap* decodedBitmap, + SkIRect region) { - config.output.colorspace = mode; - config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); - config.output.u.RGBA.stride = decodedBitmap->rowBytes(); - config.output.u.RGBA.size = decodedBitmap->getSize(); - config.output.is_external_memory = 1; + if (!webp_get_config_resize(config, decodedBitmap, + region.width(), region.height())) return false; - if (origWidth != decodedBitmap->width() || - origHeight != decodedBitmap->height()) { - config.options.use_scaling = 1; - config.options.scaled_width = decodedBitmap->width(); - config.options.scaled_height = decodedBitmap->height(); - } + config.options.use_cropping = 1; + config.options.crop_left = region.fLeft; + config.options.crop_top = region.fTop; + config.options.crop_width = region.width(); + config.options.crop_height = region.height(); return true; } bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) { - bool hasAlpha = false; SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha); // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. @@ -286,16 +275,15 @@ bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, decodedBitmap->setConfig(config, width, height, 0); - // Current WEBP specification has no support for alpha layer. - decodedBitmap->setIsOpaque(true); + decodedBitmap->setIsOpaque(!hasAlpha); return true; } bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream, int *width, int *height) { - int origWidth, origHeight; - if (!webp_parse_header(stream, &origWidth, &origHeight)) { + int origWidth, origHeight, hasAlpha; + if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { return false; } @@ -306,11 +294,12 @@ bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream, this->inputStream = stream; this->origWidth = origWidth; this->origHeight = origHeight; + this->hasAlpha = hasAlpha; return true; } -static bool isConfigCompatiable(SkBitmap* bitmap) { +static bool isConfigCompatible(SkBitmap* bitmap) { SkBitmap::Config config = bitmap->config(); return config == SkBitmap::kARGB_4444_Config || config == SkBitmap::kRGB_565_Config || @@ -338,9 +327,9 @@ bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap, // 3. bitmap's size is same as the required region (after sampled) bool directDecode = (rect == region) && (decodedBitmap->isNull() || - isConfigCompatiable(decodedBitmap) && + (isConfigCompatible(decodedBitmap) && (decodedBitmap->width() == width) && - (decodedBitmap->height() == height)); + (decodedBitmap->height() == height))); SkTScopedPtr adb; SkBitmap *bitmap = decodedBitmap; @@ -394,10 +383,11 @@ bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, AutoTimeMillis atm("WEBP Decode"); #endif - int origWidth, origHeight; - if (!webp_parse_header(stream, &origWidth, &origHeight)) { + int origWidth, origHeight, hasAlpha; + if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { return false; } + this->hasAlpha = hasAlpha; const int sampleSize = this->getSampleSize(); SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); @@ -568,8 +558,8 @@ bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, #include "SkTRegistry.h" static SkImageDecoder* DFactory(SkStream* stream) { - int width, height; - if (!webp_parse_header(stream, &width, &height)) { + int width, height, hasAlpha; + if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { return false; } -- cgit v1.1 From 79bf51281ffa537ddfbb54dededdad5aa9e466e8 Mon Sep 17 00:00:00 2001 From: Victoria Lease Date: Wed, 13 Jun 2012 10:23:10 -0700 Subject: Skia solution to fake-bold fallback fonts. This version of the fake-bold-for-fallback-fonts fix addresses the issue succinctly inside of Skia, with no need for Skia's clients to be aware of the issue. Attempts to draw a bold parent font flag the embolden flag in the associated SkScalerContext while leaving the SkPaint's fake bold flag unmodified. This depends upon change Icdd13f6b, which arranges for FreeType's FontHost to ignore redundant embolden requests. Bug: 6629786 Change-Id: I4132a232896f72f632caefd79add2f00c36a578a --- src/core/SkPaint.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 07fe9b0..e1932a7 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -1447,7 +1447,8 @@ void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec) { SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective()); - rec->fOrigFontID = SkTypeface::UniqueID(paint.getTypeface()); + SkTypeface* typeface = paint.getTypeface(); + rec->fOrigFontID = SkTypeface::UniqueID(typeface); rec->fFontID = rec->fOrigFontID; rec->fTextSize = paint.getTextSize(); rec->fPreScaleX = paint.getTextScaleX(); @@ -1468,10 +1469,21 @@ void SkScalerContext::MakeRec(const SkPaint& paint, unsigned flags = 0; - if (paint.isFakeBoldText()) { #ifdef SK_USE_FREETYPE_EMBOLDEN + // It is possible that the SkTypeface used to draw glyphs has + // different properties than the SkTypeface set in the SkPaint. + // If we are asked to render bold text with a bold font, and are + // forced to fall back to a font with normal weight for some + // glyphs, we need to use fake bold to render those glyphs. In + // order to do that, we set SkScalerContext's "embolden" flag + // here if we are trying to draw bold text via any means, and + // ignore it at the glyph outline generation stage if the font + // actually being used is already bold. + if (paint.isFakeBoldText() || (typeface && typeface->isBold())) { flags |= SkScalerContext::kEmbolden_Flag; + } #else + if (paint.isFakeBoldText()) { SkScalar fakeBoldScale = SkScalarInterpFunc(paint.getTextSize(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, @@ -1484,8 +1496,8 @@ void SkScalerContext::MakeRec(const SkPaint& paint, } else { strokeWidth += extra; } -#endif } +#endif if (paint.isDevKernText()) { flags |= SkScalerContext::kDevKernText_Flag; -- cgit v1.1 From 421654d4bf3ca50e2247a7389851d2e8f4812564 Mon Sep 17 00:00:00 2001 From: Billy Hewlett Date: Mon, 11 Jun 2012 15:52:55 -0700 Subject: Use Elegant fonts for Webkit, Compact fonts for Textview Fonts can be marked with elegant or compact in fallback_fonts.xml. Webkit uses elegant fonts, Textview uses compact fonts (the default), unmarked fonts are used by both. Bug: 6649136 Change-Id: I6ab6173c2efc50eba8fcc488c89dfdd083fb9c06 --- src/core/SkPaint.cpp | 16 ++ src/core/SkScalerContext.cpp | 8 +- src/ports/FontHostConfiguration_android.cpp | 53 +++++- src/ports/FontHostConfiguration_android.h | 17 +- src/ports/SkFontHost_android.cpp | 266 +++++++++++++++------------- 5 files changed, 228 insertions(+), 132 deletions(-) (limited to 'src') 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 #if !defined(SK_BUILD_FOR_ANDROID_NDK) #include @@ -46,12 +47,13 @@ struct FamilyData { XML_Parser *parser; // The expat parser doing the work SkTDArray &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 fNames; - SkTDArray fFileNames; + SkTDArray fNames; + SkTDArray 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 #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 gSystemFonts; -static SkTDArray gFallbackFonts; +static SkTDArray 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 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( + (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; } - -- cgit v1.1 From 81957ccb58db76c172e195dc3e31eba85d3e4ac9 Mon Sep 17 00:00:00 2001 From: Billy Hewlett Date: Thu, 21 Jun 2012 09:45:15 -0700 Subject: Revert "Use Elegant fonts for Webkit, Compact fonts for Textview" This reverts commit 421654d4bf3ca50e2247a7389851d2e8f4812564 --- src/core/SkPaint.cpp | 16 -- src/core/SkScalerContext.cpp | 8 +- src/ports/FontHostConfiguration_android.cpp | 53 +----- src/ports/FontHostConfiguration_android.h | 17 +- src/ports/SkFontHost_android.cpp | 266 +++++++++++++--------------- 5 files changed, 132 insertions(+), 228 deletions(-) (limited to 'src') diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 412ab2b..e1932a7 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -72,7 +72,6 @@ SkPaint::SkPaint() { fHinting = SkPaintDefaults_Hinting; #ifdef SK_BUILD_FOR_ANDROID new(&fTextLocale) SkString(); - fFontVariant = kDefault_Variant; fGenerationID = 0; #endif } @@ -373,18 +372,6 @@ 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 /////////////////////////////////////////////////////////////////////////////// @@ -1574,9 +1561,6 @@ 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 e33ad7a..2921b1e 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -118,13 +118,7 @@ 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) -#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 - + uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID); if (0 == newFontID) { return NULL; } diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp index 9296769..d1164c8 100644 --- a/src/ports/FontHostConfiguration_android.cpp +++ b/src/ports/FontHostConfiguration_android.cpp @@ -18,7 +18,6 @@ #include "FontHostConfiguration_android.h" #include "SkString.h" #include "SkTDArray.h" -#include "SkTypeface.h" #include #if !defined(SK_BUILD_FOR_ANDROID_NDK) #include @@ -47,13 +46,12 @@ struct FamilyData { XML_Parser *parser; // The expat parser doing the work SkTDArray &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 FontFileInfo arrays. + * or file tag. The resulting strings are put into the fNames or fFileNames arrays. */ void textHandler(void *data, const char *s, int len) { FamilyData *familyData = (FamilyData*) data; @@ -70,9 +68,7 @@ void textHandler(void *data, const char *s, int len) { *(familyData->currentFamily->fNames.append()) = buff; break; case FILESET_TAG: - if (familyData->currentFontInfo) { - familyData->currentFontInfo->fFileName = buff; - } + *(familyData->currentFamily->fFileNames.append()) = buff; break; default: // Noop - don't care about any text that's not in the Fonts or Names list @@ -82,39 +78,6 @@ 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. */ @@ -135,16 +98,14 @@ 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) { + } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || + (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_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); } } @@ -159,9 +120,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 c8a70e3..2441f0e 100644 --- a/src/ports/FontHostConfiguration_android.h +++ b/src/ports/FontHostConfiguration_android.h @@ -18,27 +18,18 @@ #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. fontFileArray is the list of information - * about each file. Order is the priority order for the font. This is + * 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 * used internally to determine the order in which to place fallback fonts as * they are read from the configuration files. */ struct FontFamily { - SkTDArray fNames; - SkTDArray fFontFileArray; + SkTDArray fNames; + SkTDArray fFileNames; int order; }; diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 0a9cced..afd0ebe 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -29,7 +29,7 @@ #include #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(const SkScalerContext::Rec& rec); +static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID); static SkTypeface* createTypefaceFromStreamLocked(SkStream* stream); /////////////////////////////////////////////////////////////////////////////// @@ -150,12 +150,6 @@ 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) { @@ -186,14 +180,6 @@ 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 */ @@ -411,15 +397,8 @@ private: // used to record our notion of the pre-existing fonts struct FontInitRec { - 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; + const char* fFileName; + const char* const* fNames; // null-terminated list }; // deliberately empty, but we use the address to identify fallback fonts @@ -431,7 +410,7 @@ static const char* gFBNames[] = { NULL }; null for the list. The names list must be NULL-terminated. */ static SkTDArray gSystemFonts; -static SkTDArray gFallbackFonts; +static SkTDArray gFallbackFonts; // these globals are assigned (once) by loadSystemFontsLocked() static FamilyRec* gDefaultFamily = NULL; @@ -514,8 +493,8 @@ static void loadFontInfoLocked() { for (int i = 0; i < fontFamilies.count(); ++i) { FontFamily *family = fontFamilies[i]; - for (int j = 0; j < family->fFontFileArray.count(); ++j) { - const char* filename = family->fFontFileArray[j]->fFileName; + for (int j = 0; j < family->fFileNames.count(); ++j) { + const char* filename = family->fFileNames[j]; if (haveSystemFont(filename)) { SkDebugf("---- system font and fallback font files specify a duplicate " "font %s, skipping the second occurrence", filename); @@ -524,7 +503,6 @@ static void loadFontInfoLocked() { FontInitRec fontInfoRecord; fontInfoRecord.fFileName = filename; - fontInfoRecord.fVariant = family->fFontFileArray[j]->fVariant; if (j == 0) { if (family->fNames.count() == 0) { // Fallback font @@ -617,10 +595,7 @@ static void initSystemFontsLocked() { 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; + *gFallbackFonts.append() = tf->uniqueID(); } firstInFamily = tf; @@ -659,7 +634,7 @@ static SkFontID findUniqueIDLocked(const char* filename) { static int findFallbackFontIndex(SkFontID fontId) { for (int i = 0; i < gFallbackFonts.count(); i++) { - if (gFallbackFonts[i].fFontID == fontId) { + if (gFallbackFonts[i] == fontId) { return i; } } @@ -677,8 +652,8 @@ static void reloadFallbackFontsLocked() { 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; + for (int j = 0; j < family->fFileNames.count(); ++j) { + const char* filename = family->fFileNames[j]; if (filename) { if (!haveSystemFont(filename)) { SkDebugf("---- skipping fallback font %s because it was not " @@ -705,10 +680,8 @@ static void reloadFallbackFontsLocked() { 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; + + *gFallbackFonts.append() = uniqueID; break; // The fallback set contains only the first font of each family } } @@ -913,16 +886,16 @@ static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int } } -SkFontID SkFontHost::NextLogicalFont(const SkScalerContext::Rec& rec) { +SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex); - return nextLogicalFontLocked(rec); + return nextLogicalFontLocked(currFontID, origFontID); } -static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) { +static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID) { loadSystemFontsLocked(); - const SkTypeface* origTypeface = findFromUniqueIDLocked(rec.fOrigFontID); - const SkTypeface* currTypeface = findFromUniqueIDLocked(rec.fFontID); + const SkTypeface* origTypeface = findFromUniqueIDLocked(origFontID); + const SkTypeface* currTypeface = findFromUniqueIDLocked(currFontID); SkASSERT(origTypeface != 0); SkASSERT(currTypeface != 0); @@ -938,30 +911,17 @@ static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) { */ int plainFallbackFontIndex = findFallbackFontIndex(plainFontID); int nextFallbackFontIndex = plainFallbackFontIndex + 1; - - // 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++; + SkFontID nextFontID; + if (nextFallbackFontIndex == gFallbackFonts.count()) { + nextFontID = 0; // no more fallbacks + } else { + const SkTypeface* nextTypeface = findFromUniqueIDLocked(gFallbackFonts[nextFallbackFontIndex]); + nextFontID = findTypefaceLocked(nextTypeface, origTypeface->style())->uniqueID(); } SkDEBUGF(("---- nextLogicalFont: currFontID=%d, origFontID=%d, plainFontID=%d, " "plainFallbackFontIndex=%d, nextFallbackFontIndex=%d " - "=> nextFontID=%d", rec.fFontID, rec.fOrigFontID, plainFontID, + "=> nextFontID=%d", currFontID, origFontID, plainFontID, plainFallbackFontIndex, nextFallbackFontIndex, nextFontID)); return nextFontID; } @@ -1006,9 +966,46 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { // Function from SkTypeface_android.h /////////////////////////////////////////////////////////////////////////////// -static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style, - SkPaint::FontVariant fontVariant) { - SkTypeface* face = FindBestFace(gDefaultFamily, style); +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(); if (!face) { return 0; } @@ -1016,7 +1013,6 @@ 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(); @@ -1029,93 +1025,71 @@ static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style, return 0; } -struct HB_UnicodeMapping { - HB_Script script; - const SkUnichar unicode; -}; - -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}, -}; +// this function can't be called if the gFamilyHeadAndNameListMutex is already locked +static void initFBScriptInfo() { + if (gFBScriptInitialized) { + return; + } -// 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; - } + // 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)); } - return unichar; + // mark the value as initialized so we don't repeat our work unnecessarily + gFBScriptInitialized = true; } -struct TypefaceLookupStruct { - HB_Script script; - SkTypeface::Style style; - SkPaint::FontVariant fontVariant; - SkTypeface* typeface; -}; +SkTypeface* SkCreateTypefaceForScript(FallbackScripts script) { + if (!SkTypeface_ValidScript(script)) { + return NULL; + } -SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable -static SkTDArray gTypefaceTable; // This is protected by gTypefaceTableMutex + // ensure that our table is populated + initFBScriptInfo(); -static int typefaceLookupCompare(const TypefaceLookupStruct& first, - const TypefaceLookupStruct& second) { - if (first.script != second.script) { - return (first.script > second.script) ? 1 : -1; - } - if (first.style != second.style) { - return (first.style > second.style) ? 1 : -1; - } - if (first.fontVariant != second.fontVariant) { - return (first.fontVariant > second.fontVariant) ? 1 : -1; + FBScriptInfo& scriptInfo = gFBScriptInfo[script]; + + // ensure the element with that index actually maps to the correct script + SkASSERT(scriptInfo.fScript == script); + + // if a suitable script could not be found then return NULL + if (scriptInfo.fFontID == 0) { + return NULL; } - return 0; + + 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; } -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( - (const TypefaceLookupStruct*) gTypefaceTable.begin(), - gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct), - &typefaceLookupCompare); - if (index >= 0) { - retTypeface = gTypefaceTable[index].typeface; +const char* SkGetFallbackScriptID(FallbackScripts script) { + for (int i = 0; i < gFBScriptInfoCount; i++) { + if (gFBScriptInfo[i].fScript == script) { + return gFBScriptInfo[i].fScriptID; + } } - else { - SkUnichar unichar = getUnicodeFromHBScript(script); - if (!unichar) { - return NULL; + return NULL; +} + +FallbackScripts SkGetFallbackScriptFromID(const char* id) { + for (int i = 0; i < gFBScriptInfoCount; i++) { + if (strcmp(gFBScriptInfo[i].fScriptID, id) == 0) { + return gFBScriptInfo[i].fScript; } - 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 retTypeface; + return kFallbackScriptNumber; // Use kFallbackScriptNumber as an invalid value. } + -- cgit v1.1 From 212ef7a5d592a11522f73d858bf98f5201631b70 Mon Sep 17 00:00:00 2001 From: Billy Hewlett Date: Thu, 21 Jun 2012 09:58:17 -0700 Subject: Revert "Revert "Use Elegant fonts for Webkit, Compact fonts for Textview"" This reverts commit 81957ccb58db76c172e195dc3e31eba85d3e4ac9 --- src/core/SkPaint.cpp | 16 ++ src/core/SkScalerContext.cpp | 8 +- src/ports/FontHostConfiguration_android.cpp | 53 +++++- src/ports/FontHostConfiguration_android.h | 17 +- src/ports/SkFontHost_android.cpp | 266 +++++++++++++++------------- 5 files changed, 228 insertions(+), 132 deletions(-) (limited to 'src') 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 #if !defined(SK_BUILD_FOR_ANDROID_NDK) #include @@ -46,12 +47,13 @@ struct FamilyData { XML_Parser *parser; // The expat parser doing the work SkTDArray &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 fNames; - SkTDArray fFileNames; + SkTDArray fNames; + SkTDArray 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 #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 gSystemFonts; -static SkTDArray gFallbackFonts; +static SkTDArray 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 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( + (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; } - -- cgit v1.1 From 669f287c3eec9ca1755d8d27ba1b5e17464b714a Mon Sep 17 00:00:00 2001 From: Billy Hewlett Date: Tue, 26 Jun 2012 13:27:55 -0700 Subject: Cleanup ref counts for SkCreateTypefaceForScript After this checkin, SkCreateTypefaceForScript refs each typeface that it creates before it returns it. Additionally, all supported harfbuzz codepoints are added to the unicode mapping array. This patch also fixes a test crash (see bug) Bug:6756432 Change-Id: Ida3276b76e745b817f9e63be54945e411516dd03 --- src/ports/SkFontHost_android.cpp | 42 ++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 0a9cced..38d1d06 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -1034,20 +1034,42 @@ struct HB_UnicodeMapping { const SkUnichar unicode; }; +/* + * The following scripts are not complex fonts and we do not expect them to be parsed by this table + * HB_Script_Common, + * HB_Script_Greek, + * HB_Script_Cyrillic, + * HB_Script_Hangul + * HB_Script_Inherited + */ + 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_Arabic, 0x0600}, + {HB_Script_Syriac, 0x0710}, + {HB_Script_Thaana, 0x0780}, + {HB_Script_Nko, 0x07C0}, + {HB_Script_Devanagari, 0x0901}, + {HB_Script_Bengali, 0x0981}, + {HB_Script_Gurmukhi, 0x0A10}, + {HB_Script_Gujarati, 0x0A90}, + {HB_Script_Oriya, 0x0B10}, + {HB_Script_Tamil, 0x0B82}, + {HB_Script_Telugu, 0x0C10}, {HB_Script_Kannada, 0x0C90}, {HB_Script_Malayalam, 0x0D10}, - {HB_Script_Tamil, 0x0B82}, + {HB_Script_Sinhala, 0x0D90}, {HB_Script_Thai, 0x0E01}, - {HB_Script_Telugu, 0x0C10}, + {HB_Script_Lao, 0x0E81}, + {HB_Script_Tibetan, 0x0F00}, + {HB_Script_Myanmar, 0x1000}, + {HB_Script_Georgian, 0x10A0}, + // we don't currently support HB_Script_Ethiopic, it is a placeholder for an upstream merge + //{HB_Script_Ethiopic, 0x1200}, + {HB_Script_Ogham, 0x1680}, + {HB_Script_Runic, 0x16A0}, + {HB_Script_Khmer, 0x1780}, }; // returns 0 for "Not Found" @@ -1111,11 +1133,11 @@ SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style 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; } + // we ref(), the caller is expected to unref when they are done + SkSafeRef(retTypeface); return retTypeface; } -- cgit v1.1 From b4fad178c792687c50683880bcfaf507d54b30ca Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 17 Jul 2012 15:36:33 -0700 Subject: Accurately calculate advances in general case. Fixes bug 6833339. The advance values computed by generateAdvance() were inconsistent with those computed by generateMetrics, because the fMatrix22 was being applied in the latter but not former case. Since fMatrix22.xx is 1.0 almost all the time (the exception is only when skew and scale are both applied), failures were rare. This patch is an alternative to ag/#/c/207887, in that they both fix the problem, but in different ways. That patch made fMatrix22.xx equal to 1.0 in almost all cases, while this one gives correct advance values for arbitrary fMatrix22 values. Change-Id: Iedfa36e884e3e3e2f078a5d4edfc82004a54e895 --- src/ports/SkFontHost_FreeType.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 621c94a..da00d51 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -951,8 +951,8 @@ void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) { if (0 == error) { glyph->fRsbDelta = 0; glyph->fLsbDelta = 0; - glyph->fAdvanceX = advance; // advance *2/3; //DEBUG - glyph->fAdvanceY = 0; + glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance); // advance *2/3; //DEBUG + glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, advance); return; } } -- cgit v1.1 From d1280a29424e20a876a4995ff1446d57aac0289d Mon Sep 17 00:00:00 2001 From: Victoria Lease Date: Thu, 26 Jul 2012 15:50:40 -0700 Subject: Reduce embolden effect. Certain CJK glyphs become essentially unreadable when emboldened at sizes used by common apps like News/Weather. Reducing embolden strength slightly makes these glyphs much more readable without sacrificing bold/normal weight differentiation. Bug: 6712857 Change-Id: Iec1c478171e33751be1cddbaae6a8ec30bb6cbab --- src/ports/SkFontHost_FreeType.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index da00d51..6aa2120 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -63,6 +63,16 @@ #define SK_GAMMA_EXPONENT 2.2 #endif +// hand-tuned value to reduce outline embolden strength +#ifndef SK_OUTLINE_EMBOLDEN_DIVISOR + #ifdef SK_BUILD_FOR_ANDROID + #define SK_OUTLINE_EMBOLDEN_DIVISOR 34 + #else + #define SK_OUTLINE_EMBOLDEN_DIVISOR 24 + #endif +#endif + + #ifdef SK_DEBUG #define SkASSERT_CONTINUE(pred) \ do { \ @@ -892,7 +902,7 @@ FT_Error SkScalerContext_FreeType::setupSize() { void SkScalerContext_FreeType::emboldenOutline(FT_Outline* outline) { FT_Pos strength; strength = FT_MulFix(fFace->units_per_EM, fFace->size->metrics.y_scale) - / 24; + / SK_OUTLINE_EMBOLDEN_DIVISOR; FT_Outline_Embolden(outline, strength); } -- cgit v1.1 From e454fde2aeb9f50cae4ae6d3237aac6553540ff5 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Fri, 27 Jul 2012 12:42:04 -0700 Subject: Fix bug 6888377: crash in GetUnitsPerEm on locale change The underlying problem is that no SkScalerContext objects existed at the time shapeFontRun is called immediately after a locale change from en to ja (apparently the dumping of the cache caused all these to be deallocated), so gFTLibrary was null (and the call tio ref_ft_face assumes that it's initialized). There's a pattern for calls which might not necessarily be called from a scaler context (GetAdvancedTypefaceMetrics is one such), to explicitly check for an uninitialized library, and create one for the length of the call if so. This patch changes GetUnitsPerEm to follow this pattern. Change-Id: I19a4b6fa49fad0aeacc04bf971101aacca6bc94f --- src/ports/SkFontHost_FreeType.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index da00d51..eb05959 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -685,6 +685,13 @@ void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { #ifdef SK_BUILD_FOR_ANDROID uint32_t SkFontHost::GetUnitsPerEm(SkFontID fontID) { SkAutoMutexAcquire ac(gFTMutex); + FT_Library libInit = NULL; + if (gFTCount == 0) { + if (!InitFreetype()) + sk_throw(); + libInit = gFTLibrary; + } + SkAutoTCallIProc ftLib(libInit); SkFaceRec *rec = ref_ft_face(fontID); uint16_t unitsPerEm = 0; -- cgit v1.1 From 99180756367573f1d741a5104ed401c5a7308ae9 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Thu, 16 Aug 2012 15:08:47 -0400 Subject: Ensure SkCanvas will have a device by default. bug: 6987261 Change-Id: I8cb5b6da2347a1fa88a13aa3548312a7773831e5 --- src/core/SkCanvas.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 7861636..0e26728 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -425,7 +425,8 @@ SkCanvas::SkCanvas() : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) { inc_canvas(); - this->init(NULL); + SkBitmap bitmap; + this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref(); } SkCanvas::SkCanvas(SkDevice* device) -- cgit v1.1 From fd3c420022d5f842fe8be5c21300e0d01463a5ad Mon Sep 17 00:00:00 2001 From: Victoria Lease Date: Fri, 17 Aug 2012 14:59:02 -0700 Subject: Forward-compatibility stubs Change-Id: I9c5bf5ce38827ced91d8912a1fa49adbd14a46b8 --- src/core/SkLanguage.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ src/core/SkPaint.cpp | 4 ++++ 2 files changed, 58 insertions(+) create mode 100644 src/core/SkLanguage.cpp (limited to 'src') 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 + +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 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 e1932a7..fc5e57c 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -372,6 +372,10 @@ void SkPaint::setTextLocale(const SkString& locale) { GEN_ID_INC; } } + +void SkPaint::setLanguage(const SkLanguage& language) { + setTextLocale(SkString(language.getTag())); +} #endif /////////////////////////////////////////////////////////////////////////////// -- cgit v1.1 From 725b3e03acb91dc69296554932624b36612bd189 Mon Sep 17 00:00:00 2001 From: Billy Hewlett Date: Tue, 3 Jul 2012 17:03:55 -0700 Subject: 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 --- src/core/SkLanguage.cpp | 54 +++++ src/core/SkPaint.cpp | 26 ++- src/core/SkScalerContext.cpp | 58 +++--- src/ports/FontHostConfiguration_android.cpp | 75 +------ src/ports/FontHostConfiguration_android.h | 18 +- src/ports/SkFontHost_android.cpp | 300 ++++++++++++++++++---------- 6 files changed, 316 insertions(+), 215 deletions(-) create mode 100644 src/core/SkLanguage.cpp (limited to 'src') 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 + +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 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