diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkCanvas.cpp | 3 | ||||
-rw-r--r-- | src/core/SkGlyphCache.h | 2 | ||||
-rw-r--r-- | src/core/SkLanguage.cpp | 54 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 42 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 7 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 66 | ||||
-rw-r--r-- | src/effects/SkGradientShader.cpp | 4 | ||||
-rw-r--r-- | src/images/SkImageDecoder_libjpeg.cpp | 2 | ||||
-rw-r--r-- | src/images/SkImageDecoder_libwebp.cpp | 120 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.cpp | 116 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.h | 27 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 64 | ||||
-rw-r--r-- | src/ports/SkFontHost_android.cpp | 499 |
13 files changed, 711 insertions, 295 deletions
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) 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/core/SkLanguage.cpp b/src/core/SkLanguage.cpp new file mode 100644 index 0000000..3b8ba3c --- /dev/null +++ b/src/core/SkLanguage.cpp @@ -0,0 +1,54 @@ + +/* + * Copyright 2012 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkLanguage.h" + +#ifdef SK_BUILD_FOR_ANDROID // currently only for Android + +#include "SkTDict.h" +#include "SkThread.h" +#include <cstring> + +SkLanguage SkLanguage::getParent() const { + SkASSERT(fInfo != NULL); + SkASSERT(fInfo->fTag != NULL); + const char* tag = fInfo->fTag.c_str(); + SkASSERT(tag != NULL); + + // strip off the rightmost "-.*" + char* parentTagEnd = strrchr(tag, '-'); + if (parentTagEnd == NULL) { + return SkLanguage(""); + } + size_t parentTagLen = parentTagEnd - tag; + char parentTag[parentTagLen + 1]; + strncpy(parentTag, tag, parentTagLen); + parentTag[parentTagLen] = '\0'; + return SkLanguage(parentTag); +} + +SK_DECLARE_STATIC_MUTEX(gGetInfoMutex); +const SkLanguageInfo* SkLanguage::getInfo(const char* tag) { + SkAutoMutexAcquire lock(gGetInfoMutex); + + static const size_t kDictSize = 128; + static SkTDict<SkLanguageInfo*> tagToInfo(kDictSize); + + // try a lookup + SkLanguageInfo* info; + if (tagToInfo.find(tag, &info)) { + return info; + } + + // no match - add this language + info = new SkLanguageInfo(tag); + tagToInfo.set(tag, info); + return info; +} + +#endif diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index e1932a7..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,8 @@ SkPaint::SkPaint() { fTextEncoding = kUTF8_TextEncoding; fHinting = SkPaintDefaults_Hinting; #ifdef SK_BUILD_FOR_ANDROID - new(&fTextLocale) SkString(); + fLanguage = SkLanguage(); + fFontVariant = kDefault_Variant; fGenerationID = 0; #endif } @@ -88,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() { @@ -129,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 @@ -366,12 +363,24 @@ 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; } } + +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 +1570,10 @@ void SkScalerContext::MakeRec(const SkPaint& paint, #else rec->setLuminanceBits(computeLuminance(paint)); #endif +#ifdef SK_BUILD_FOR_ANDROID + rec->fLanguage = paint.getLanguage(); + 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, @@ -1836,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) { @@ -1890,6 +1909,11 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF)); this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF)); +#ifdef SK_BUILD_FOR_ANDROID + this->setFontVariant(SkPaint::FontVariant(buffer.readInt())); + this->setLanguage(SkLanguage(buffer.readString())); +#endif + if (flatFlags & kHasTypeface_FlatFlag) { this->setTypeface(buffer.readTypeface()); } else { diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 7f58ae3..3436996 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -1208,6 +1208,7 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { } } + // swap() will increment the gen id if needed dst->swap(tmp); matrix.mapPoints(dst->fPts.begin(), dst->fPts.count()); } else { @@ -1218,7 +1219,6 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { matrix.mapRect(&dst->fBounds, fBounds); dst->fBoundsIsDirty = false; } else { - GEN_ID_PTR_INC(dst); dst->fBoundsIsDirty = true; } @@ -1229,7 +1229,12 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { dst->fSegmentMask = fSegmentMask; dst->fConvexity = fConvexity; } + + if (!matrix.isIdentity()) { + GEN_ID_PTR_INC(dst); + } matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count()); + SkDEBUGCODE(dst->validate();) } } diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index 2921b1e..eee0dc5 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; } @@ -156,6 +162,21 @@ SkScalerContext* SkScalerContext::getNextContext() { return next; } +SkScalerContext* SkScalerContext::getContextFromChar(SkUnichar uni, unsigned& glyphID) { + SkScalerContext* ctx = this; + for (;;) { + glyphID = ctx->generateCharToGlyph(uni); + if (glyphID) { + break; // found it + } + ctx = ctx->getNextContext(); + if (NULL == ctx) { + return NULL; + } + } + return ctx; +} + SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) { unsigned glyphID = glyph.getGlyphID(); SkScalerContext* ctx = this; @@ -176,6 +197,16 @@ SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) { } #ifdef SK_BUILD_FOR_ANDROID +SkFontID SkScalerContext::findTypefaceIdForChar(SkUnichar uni) { + unsigned glyphID; + SkScalerContext* ctx = getContextFromChar(uni, glyphID); + if (ctx) { + return ctx->fRec.fFontID; + } else { + return 0; + } +} + /* This loops through all available fallback contexts (if needed) until it finds some context that can handle the unichar and return it. @@ -183,21 +214,14 @@ SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) { char of a run. */ unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) { - SkScalerContext* ctx = this; unsigned glyphID; - for (;;) { - glyphID = ctx->generateCharToGlyph(uni); - if (glyphID) { - break; // found it - } - ctx = ctx->getNextContext(); - if (NULL == ctx) { - SkDebugf("--- no context for char %x\n", uni); - // just return the original context (this) - return this->fBaseGlyphCount; - } + SkScalerContext* ctx = getContextFromChar(uni, glyphID); + if (ctx) { + return ctx->fBaseGlyphCount; + } else { + SkDEBUGF(("--- no context for char %x\n", uni)); + return this->fBaseGlyphCount; } - return ctx->fBaseGlyphCount; } #endif @@ -205,17 +229,11 @@ unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) { finds some context that can handle the unichar. If all fail, returns 0 */ uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) { - SkScalerContext* ctx = this; + unsigned glyphID; - for (;;) { - glyphID = ctx->generateCharToGlyph(uni); - if (glyphID) { - break; // found it - } - ctx = ctx->getNextContext(); - if (NULL == ctx) { - return 0; // no more contexts, return missing glyph - } + SkScalerContext* ctx = getContextFromChar(uni, glyphID); + if (!ctx) { + return 0; // no more contexts, return missing glyph } // add the ctx's base, making glyphID unique for chain of contexts glyphID += ctx->fBaseGlyphCount; diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp index 4bbc96c..24ed445 100644 --- a/src/effects/SkGradientShader.cpp +++ b/src/effects/SkGradientShader.cpp @@ -1046,7 +1046,9 @@ void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, } LinearShadeProc shadeProc = shadeSpan_linear_repeat; - if (SkFixedNearlyZero(dx)) { + // We really should check the endpoint colors, but short of that change + // we reduce the tolerance of SkFixedNearlyZero to be more restrictive. + if (SkFixedNearlyZero(dx, (SK_Fixed1 >> 14))) { #ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS if (fColorCount > 2) { shadeProc = shadeSpan_linear_vertical_lerp; diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index fbb6887..33f222c 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -48,9 +48,9 @@ public: SkJPEGImageIndex() {} virtual ~SkJPEGImageIndex() { jpeg_destroy_huffman_index(index); - delete cinfo->src; jpeg_finish_decompress(cinfo); jpeg_destroy_decompress(cinfo); + delete cinfo->src; free(cinfo); } jpeg_decompress_struct *cinfo; diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp index 3e416cc..3ca1254 100644 --- a/src/images/SkImageDecoder_libwebp.cpp +++ b/src/images/SkImageDecoder_libwebp.cpp @@ -34,7 +34,6 @@ extern "C" { // If moving libwebp out of skia source tree, path for webp headers must be // updated accordingly. Here, we enforce using local copy in webp sub-directory. #include "webp/decode.h" -#include "webp/decode_vp8.h" #include "webp/encode.h" } @@ -56,20 +55,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 +110,7 @@ private: SkStream *inputStream; int origWidth; int origHeight; + int hasAlpha; }; ////////////////////////////////////////////////////////////////////////// @@ -136,16 +145,20 @@ static bool return_false(const SkBitmap& bm, const char msg[]) { return false; // must always return false } -static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap) { +static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap, int hasAlpha) { WEBP_CSP_MODE mode = MODE_LAST; SkBitmap::Config config = decodedBitmap->config(); + // For images that have alpha, choose appropriate color mode (MODE_rgbA, + // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency + // factor (alpha). if (config == SkBitmap::kARGB_8888_Config) { - mode = MODE_RGBA; + mode = hasAlpha ? MODE_rgbA : MODE_RGBA; } else if (config == SkBitmap::kARGB_4444_Config) { - mode = MODE_RGBA_4444; + mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444; } else if (config == SkBitmap::kRGB_565_Config) { mode = MODE_RGB_565; } + SkASSERT(mode != MODE_LAST); return mode; } @@ -160,10 +173,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 +186,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,10 +212,10 @@ static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) { } } -static bool webp_get_config_resize_crop(WebPDecoderConfig& config, - SkBitmap* decodedBitmap, - SkIRect region) { - WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap); +static bool webp_get_config_resize(WebPDecoderConfig& config, + SkBitmap* decodedBitmap, + int width, int height, int hasAlpha) { + WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha); if (mode == MODE_LAST) { return false; } @@ -219,14 +230,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 +240,26 @@ 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; - } +static bool webp_get_config_resize_crop(WebPDecoderConfig& config, + SkBitmap* decodedBitmap, + SkIRect region, int hasAlpha) { - if (WebPInitDecoderConfig(&config) == 0) { - return false; + if (!webp_get_config_resize( + config, decodedBitmap, region.width(), region.height(), hasAlpha)) { + return false; } - 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 (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 +280,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 +299,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 +332,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<SkBitmap> adb; SkBitmap *bitmap = decodedBitmap; @@ -371,7 +365,7 @@ bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap, SkAutoLockPixels alp(*bitmap); WebPDecoderConfig config; - if (!webp_get_config_resize_crop(config, bitmap, rect)) { + if (!webp_get_config_resize_crop(config, bitmap, rect, hasAlpha)) { return false; } @@ -394,10 +388,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); @@ -428,7 +423,8 @@ bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, SkAutoLockPixels alp(*decodedBitmap); WebPDecoderConfig config; - if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight)) { + if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight, + hasAlpha)) { return false; } @@ -568,9 +564,9 @@ 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)) { - return false; + int width, height, hasAlpha; + if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { + return NULL; } // Magic matches, call decoder diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp index d1164c8..420ad1c 100644 --- a/src/ports/FontHostConfiguration_android.cpp +++ b/src/ports/FontHostConfiguration_android.cpp @@ -16,8 +16,9 @@ */ #include "FontHostConfiguration_android.h" -#include "SkString.h" +#include "SkLanguage.h" #include "SkTDArray.h" +#include "SkTypeface.h" #include <expat.h> #if !defined(SK_BUILD_FOR_ANDROID_NDK) #include <cutils/properties.h> @@ -27,7 +28,6 @@ #define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" #define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" - // These defines are used to determine the kind of tag that we're currently // populating with data. We only care about the sibling tags nameset and fileset // for now. @@ -46,12 +46,13 @@ struct FamilyData { XML_Parser *parser; // The expat parser doing the work SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed FontFamily *currentFamily; // The current family being created + FontFileInfo *currentFontInfo; // The current fontInfo being created int currentTag; // A flag to indicate whether we're in nameset/fileset tags }; /** * Handler for arbitrary text. This is used to parse the text inside each name - * or file tag. The resulting strings are put into the fNames or fFileNames arrays. + * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays. */ void textHandler(void *data, const char *s, int len) { FamilyData *familyData = (FamilyData*) data; @@ -68,7 +69,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 +81,37 @@ 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, "lang", nameLength) == 0) { + newFileInfo->fLanguage = SkLanguage(attributeValue); + } + //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 +132,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 +156,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)) { @@ -131,65 +167,6 @@ void endElementHandler(void *data, const char *tag) { } } -#if !defined(SK_BUILD_FOR_ANDROID_NDK) -/** - * Read the persistent locale. - */ -void getLocale(char* language, char* region) -{ - char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX]; - - property_get("persist.sys.language", propLang, ""); - property_get("persist.sys.country", propRegn, ""); - if (*propLang == 0 && *propRegn == 0) { - /* Set to ro properties, default is en_US */ - property_get("ro.product.locale.language", propLang, "en"); - property_get("ro.product.locale.region", propRegn, "US"); - } - strncat(language, propLang, 2); - strncat(region, propRegn, 2); -} -#endif - -/** - * Use the current system locale (language and region) to open the best matching - * customization. For example, when the language is Japanese, the sequence might be: - * /system/etc/fallback_fonts-ja-JP.xml - * /system/etc/fallback_fonts-ja.xml - * /system/etc/fallback_fonts.xml - */ -FILE* openLocalizedFile(const char* origname) { - FILE* file = 0; - -#if !defined(SK_BUILD_FOR_ANDROID_NDK) - SkString basename; - SkString filename; - char language[3] = ""; - char region[3] = ""; - - basename.set(origname); - // Remove the .xml suffix. We'll add it back in a moment. - if (basename.endsWith(".xml")) { - basename.resize(basename.size()-4); - } - getLocale(language, region); - // Try first with language and region - filename.printf("%s-%s-%s.xml", basename.c_str(), language, region); - file = fopen(filename.c_str(), "r"); - if (!file) { - // If not found, try next with just language - filename.printf("%s-%s.xml", basename.c_str(), language); - file = fopen(filename.c_str(), "r"); - } -#endif - - if (!file) { - // If still not found, try just the original name - file = fopen(origname, "r"); - } - return file; -} - /** * This function parses the given filename and stores the results in the given * families array. @@ -199,7 +176,7 @@ void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) { FamilyData *familyData = new FamilyData(&parser, families); XML_SetUserData(parser, familyData); XML_SetElementHandler(parser, startElementHandler, endElementHandler); - FILE *file = openLocalizedFile(filename); + FILE *file = fopen(filename, "r"); // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml) // are optional - failure here is okay because one of these optional files may not exist. if (file == NULL) { @@ -215,6 +192,7 @@ void parseConfigFile(const char *filename, SkTDArray<FontFamily*> &families) { } XML_Parse(parser, buffer, len, done); } + fclose(file); } void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies) { diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h index 2441f0e..6734b08 100644 --- a/src/ports/FontHostConfiguration_android.h +++ b/src/ports/FontHostConfiguration_android.h @@ -17,19 +17,33 @@ #ifndef FONTHOSTCONFIGURATION_ANDROID_H_ #define FONTHOSTCONFIGURATION_ANDROID_H_ +#include "SkTypes.h" + +#include "SkLanguage.h" +#include "SkPaint.h" #include "SkTDArray.h" +struct FontFileInfo { + FontFileInfo() : fFileName(NULL), fVariant(SkPaint::kDefault_Variant), + fLanguage() { + } + + const char* fFileName; + SkPaint::FontVariant fVariant; + SkLanguage fLanguage; +}; + /** * The FontFamily data structure is created during parsing and handed back to * Skia to fold into its representation of font families. fNames is the list of - * font names that alias to a font family. fFileNames is the list of font - * filenames for the family. Order is the priority order for the font. This is + * font names that alias to a font family. fontFileArray is the list of information + * about each file. Order is the priority order for the font. This is * used internally to determine the order in which to place fallback fonts as * they are read from the configuration files. */ struct FontFamily { - SkTDArray<const char*> fNames; - SkTDArray<const char*> fFileNames; + SkTDArray<const char*> fNames; + SkTDArray<FontFileInfo*> fFontFileArray; int order; }; @@ -45,15 +59,10 @@ void getFontFamilies(SkTDArray<FontFamily*> &fontFamilies); */ void getSystemFontFamilies(SkTDArray<FontFamily*> &fontFamilies); - /** * Parse the fallback and vendor system font configuration files and return the * results in an array of FontFamily structures. */ void getFallbackFontFamilies(SkTDArray<FontFamily*> &fallbackFonts); -#if !defined(SK_BUILD_FOR_ANDROID_NDK) - void getLocale(char* language, char* region); -#endif - #endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */ diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 3277f50..ea24a6e 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -176,6 +176,7 @@ private: FT_Matrix fMatrix22; uint32_t fLoadGlyphFlags; bool fDoLinearMetrics; + bool fUseVertMetrics; FT_Error setupSize(); void emboldenOutline(FT_Outline* outline); @@ -704,6 +705,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<struct FT_LibraryRec_, FT_Done_FreeType> ftLib(libInit); SkFaceRec *rec = ref_ft_face(fontID); uint16_t unitsPerEm = 0; @@ -779,6 +787,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) fScaleY = SkScalarToFixed(sy); // compute the flags we send to Load_Glyph + fUseVertMetrics = false; { FT_Int32 loadFlags = FT_LOAD_DEFAULT; bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); @@ -834,6 +843,12 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) // See http://code.google.com/p/skia/issues/detail?id=222. loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + // Use vertical layout if requested and supported. + if ((fRec.fFlags & SkScalerContext::kVertical_Flag) && FT_HAS_VERTICAL(fFace)) { + loadFlags |= FT_LOAD_VERTICAL_LAYOUT; + fUseVertMetrics = true; + } + fLoadGlyphFlags = loadFlags; fDoLinearMetrics = linearMetrics; } @@ -969,8 +984,8 @@ void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) { if (0 == error) { glyph->fRsbDelta = 0; glyph->fLsbDelta = 0; - glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance); - glyph->fAdvanceY = - SkFixedMul(fMatrix22.yx, advance); + glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance); // advance *2/3; //DEBUG + glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, advance); return; } } @@ -1003,6 +1018,20 @@ void SkScalerContext_FreeType::getBBoxForCurrentGlyph(SkGlyph* glyph, bbox->xMax = (bbox->xMax + 63) & ~63; bbox->yMax = (bbox->yMax + 63) & ~63; } + + // Must come after snapToPixelBoundary so that the width and height are + // consistent. Otherwise asserts will fire later on when generating the + // glyph image. + if (fUseVertMetrics) { + FT_Vector vector; + vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX; + vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY; + FT_Vector_Transform(&vector, &fMatrix22); + bbox->xMin += vector.x; + bbox->xMax += vector.x; + bbox->yMin += vector.y; + bbox->yMax += vector.y; + } } void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) { @@ -1073,6 +1102,16 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { FT_GlyphSlot_Own_Bitmap(fFace->glyph); FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0); } + + if (fUseVertMetrics) { + FT_Vector vector; + vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX; + vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY; + FT_Vector_Transform(&vector, &fMatrix22); + fFace->glyph->bitmap_left += SkFDot6Floor(vector.x); + fFace->glyph->bitmap_top += SkFDot6Floor(vector.y); + } + glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); @@ -1097,8 +1136,17 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } } - if ((fRec.fFlags & SkScalerContext::kVertical_Flag) - && fFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + if (fUseVertMetrics) { + if (fDoLinearMetrics) { + glyph->fAdvanceX = -SkFixedMul(fMatrix22.xy, fFace->glyph->linearVertAdvance); + glyph->fAdvanceY = SkFixedMul(fMatrix22.yy, fFace->glyph->linearVertAdvance); + } else { + glyph->fAdvanceX = -SkFDot6ToFixed(fFace->glyph->advance.x); + glyph->fAdvanceY = SkFDot6ToFixed(fFace->glyph->advance.y); + } + + } else if ((fRec.fFlags & SkScalerContext::kVertical_Flag) + && fFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { //TODO: do we need to specially handle SubpixelPositioning and Kerning? @@ -1561,6 +1609,14 @@ void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, emboldenOutline(&fFace->glyph->outline); } + if (fUseVertMetrics) { + FT_Vector vector; + vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX; + vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY; + FT_Vector_Transform(&vector, &fMatrix22); + FT_Outline_Translate(&fFace->glyph->outline, vector.x, vector.y); + } + FT_Outline_Funcs funcs; funcs.move_to = move_proc; diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 2c58079..dddadd0 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -27,6 +27,12 @@ #include "FontHostConfiguration_android.h" #include <stdio.h> #include <string.h> +#include "SkGlyphCache.h" +#include "SkLanguage.h" +#include "SkTypeface_android.h" +#include "SkTArray.h" +#include "SkTDict.h" +#include "SkTSearch.h" //#define SkDEBUGF(args ) SkDebugf args @@ -73,9 +79,10 @@ 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); + /////////////////////////////////////////////////////////////////////////////// struct FamilyRec; @@ -147,6 +154,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) { @@ -177,6 +190,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 */ @@ -394,26 +415,40 @@ 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; + SkLanguage fLanguage; +}; + +//used to record information about the fallback fonts +struct FallbackFontRec { + SkFontID fFontID; + SkPaint::FontVariant fVariant; +}; + +struct FallbackFontList { + FallbackFontList(const SkLanguage& language) : fLanguage(language) { } + SkTDArray<FallbackFontRec> fList; + SkLanguage fLanguage; }; // deliberately empty, but we use the address to identify fallback fonts static const char* gFBNames[] = { NULL }; - /* Fonts are grouped by family, with the first font in a family having the list of names (even if that list is empty), and the following members having null for the list. The names list must be NULL-terminated. */ -static SkTDArray<FontInitRec> gSystemFonts; -static SkTDArray<SkFontID> gFallbackFonts; +static SkTArray<FontInitRec> gSystemFonts; +static SkTDArray<FallbackFontList*> gFallbackFontLists; // these globals are assigned (once) by loadSystemFontsLocked() static FamilyRec* gDefaultFamily = NULL; static SkTypeface* gDefaultNormal = NULL; static char** gDefaultNames = NULL; +static FallbackFontList* getFallbackFontListLocked(const SkLanguage& lang); static void dumpGlobalsLocked() { SkDebugf("gDefaultNormal=%p id=%u refCnt=%d", gDefaultNormal, gDefaultNormal ? gDefaultNormal->uniqueID() : 0, @@ -434,8 +469,11 @@ static void dumpGlobalsLocked() { SkDebugf("gDefaultFamily=%p", gDefaultFamily); } - SkDebugf("gSystemFonts.count()=%d gFallbackFonts.count()=%d", - gSystemFonts.count(), gFallbackFonts.count()); + FallbackFontList* defaultFallbackList = + getFallbackFontListLocked(SkLanguage()); + SkASSERT(defaultFallbackList != NULL); + SkDebugf("gSystemFonts.count()=%d defaultFallbackList->fList.count()=%d", + gSystemFonts.count(), defaultFallbackList->fList.count()); for (int i = 0; i < gSystemFonts.count(); ++i) { SkDebugf("gSystemFonts[%d] fileName=%s", i, gSystemFonts[i].fFileName); @@ -480,9 +518,164 @@ static bool haveSystemFont(const char* filename) { return false; } +// (SkLanguage)<->(fallback chain index) translation +static const size_t kLangDictSize = 128; +static SkTDict<FallbackFontList*> gLangTagToFallbackFontList(kLangDictSize); +static bool gIsOKToUseFallbackFontListCache = false; + +// crawl fallback font lists by hand looking for a specific language +static FallbackFontList* getFallbackFontListNoCacheLocked( + const SkLanguage& lang) { + unsigned int numLists = gFallbackFontLists.count(); + for (unsigned int listIdx = 0; listIdx < numLists; ++listIdx) { + FallbackFontList* list = gFallbackFontLists[listIdx]; + SkASSERT(list != NULL); + if (list->fLanguage == lang) { + return list; + } + } + return NULL; +} + +// perform fancy fuzzy-matching memoized query for a fallback font list. +// should only be called after fallback font lists are fully loaded. +static FallbackFontList* getFallbackFontListLocked(const SkLanguage& lang) { + SkASSERT(gIsOKToUseFallbackFontListCache); + const SkString& langTag = lang.getTag(); + FallbackFontList* fallbackFontList; + if (gLangTagToFallbackFontList.find(langTag.c_str(), langTag.size(), + &fallbackFontList)) { + // cache hit! + return fallbackFontList; + } + + // try again without the cache + fallbackFontList = getFallbackFontListNoCacheLocked(lang); + if (fallbackFontList != NULL) { + // found it - cache and return + gLangTagToFallbackFontList.set(langTag.c_str(), langTag.size(), + fallbackFontList); + SkDEBUGF(("new fallback cache entry: \"%s\"", langTag.c_str())); + return fallbackFontList; + } + + // no hit - can we fuzzy-match? + if (lang.getTag().isEmpty()) { + // nope! this happens if attempting to direct match with no default + return NULL; + } + + // attempt fuzzy match + SkLanguage parent = lang.getParent(); + fallbackFontList = getFallbackFontListLocked(parent); + if (fallbackFontList != NULL) { + // found it - cache and return + gLangTagToFallbackFontList.set(langTag.c_str(), langTag.size(), + fallbackFontList); + SkDEBUGF(("new fallback cache entry: \"%s\" -> \"%s\"", langTag.c_str(), + fallbackFontList->fLanguage.getTag().c_str())); + return fallbackFontList; + } + + // utter failure. this happens if attempting to fuzzy-match with no default + SkASSERT(fallbackFontList != NULL); + return NULL; +} + +// creates a new fallback font list for the specified language +static FallbackFontList* createFallbackFontListLocked(const SkLanguage& lang) { + SkASSERT(!gIsOKToUseFallbackFontListCache); + SkDEBUGF(("new fallback list: \"%s\"", lang.getTag().c_str())); + FallbackFontList* fallbackFontList = new FallbackFontList(lang); + gFallbackFontLists.push(fallbackFontList); + return fallbackFontList; +} + +// adds a fallback font record to both the default fallback chain and the +// language-specific fallback chain to which it belongs, if any +static void addFallbackFontLocked(const FallbackFontRec& fallbackRec, + const SkLanguage& lang) { + SkASSERT(!gIsOKToUseFallbackFontListCache); + SkDEBUGF(("new fallback font: %d, in \"%s\"", fallbackRec.fFontID, + lang.getTag().c_str())); + // add to the default fallback list + FallbackFontList* fallbackList = + getFallbackFontListNoCacheLocked(SkLanguage()); + if (fallbackList == NULL) { + // oops! no default list yet. create one. + fallbackList = createFallbackFontListLocked(SkLanguage()); + } + SkASSERT(fallbackList != NULL); + fallbackList->fList.push(fallbackRec); + if (lang.getTag().isEmpty()) { + return; + } + // also add to the appropriate language's fallback list + fallbackList = getFallbackFontListNoCacheLocked(lang); + if (fallbackList == NULL) { + // first entry for this list! + fallbackList = createFallbackFontListLocked(lang); + } + SkASSERT(fallbackList != NULL); + fallbackList->fList.push(fallbackRec); +} + +static int getSystemFontIndexForFontID(SkFontID fontID) { + // font unique id = one-based index in system font table + SkASSERT(fontID - 1 < gSystemFonts.count()); + return fontID - 1; +} + +// scans the default fallback font chain, adding every entry to every other +// fallback font chain to which it does not belong. this results in every +// language-specific fallback font chain having all of its fallback fonts at +// the front of the chain, and everything else at the end. after this has been +// run, it is ok to use the fallback font chain lookup table. +static void finaliseFallbackFontListsLocked() { + SkASSERT(!gIsOKToUseFallbackFontListCache); + // if we have more than one list, we need to finalise non-default lists + unsigned int numLists = gFallbackFontLists.count(); + if (numLists > 1) { + // pull fonts off of the default list... + FallbackFontList* defaultList = getFallbackFontListNoCacheLocked( + SkLanguage()); + SkASSERT(defaultList != NULL); + int numDefaultFonts = defaultList->fList.count(); + for (int fontIdx = 0; fontIdx < numDefaultFonts; ++fontIdx) { + // figure out which language they represent + SkFontID fontID = defaultList->fList[fontIdx].fFontID; + int sysFontIdx = getSystemFontIndexForFontID(fontID); + const SkLanguage& lang = gSystemFonts[sysFontIdx].fLanguage; + for (unsigned int listIdx = 0; listIdx < numLists; ++listIdx) { + // and add them to every other language's list + FallbackFontList* thisList = gFallbackFontLists[listIdx]; + SkASSERT(thisList != NULL); + if (thisList != defaultList && thisList->fLanguage != lang) { + thisList->fList.push(defaultList->fList[fontIdx]); + } + } + } + } + gIsOKToUseFallbackFontListCache = true; +} + +static void resetFallbackFontListsLocked() { + // clear cache + gLangTagToFallbackFontList.reset(); + // clear the data it pointed at + int numFallbackLists = gFallbackFontLists.count(); + for (int fallbackIdx = 0; fallbackIdx < numFallbackLists; ++fallbackIdx) { + delete gFallbackFontLists[fallbackIdx]; + } + gFallbackFontLists.reset(); + gIsOKToUseFallbackFontListCache = false; +} + /* Load info from a configuration file that populates the system/fallback font structures */ static void loadFontInfoLocked() { + resetFallbackFontListsLocked(); + SkTDArray<FontFamily*> fontFamilies; getFontFamilies(fontFamilies); @@ -490,8 +683,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); @@ -500,6 +693,8 @@ static void loadFontInfoLocked() { FontInitRec fontInfoRecord; fontInfoRecord.fFileName = filename; + fontInfoRecord.fVariant = family->fFontFileArray[j]->fVariant; + fontInfoRecord.fLanguage = family->fFontFileArray[j]->fLanguage; if (j == 0) { if (family->fNames.count() == 0) { // Fallback font @@ -524,7 +719,7 @@ static void loadFontInfoLocked() { } else { fontInfoRecord.fNames = NULL; } - *gSystemFonts.append() = fontInfoRecord; + gSystemFonts.push_back(fontInfoRecord); } } fontFamilies.deleteAll(); @@ -535,7 +730,6 @@ static void loadFontInfoLocked() { } } - /* * Called once (ensured by the sentinel check at the beginning of our body). * Initializes all the globals, and register the system fonts. @@ -550,8 +744,6 @@ static void initSystemFontsLocked() { loadFontInfoLocked(); - gFallbackFonts.reset(); - SkTypeface* firstInFamily = NULL; for (int i = 0; i < gSystemFonts.count(); i++) { // if we're the first in a new family, clear firstInFamily @@ -590,9 +782,11 @@ static void initSystemFontsLocked() { if (names != NULL) { // see if this is one of our fallback fonts if (names == gFBNames) { - SkDEBUGF(("---- adding %s as fallback[%d] fontID %d\n", - gSystemFonts[i].fFileName, gFallbackFonts.count(), tf->uniqueID())); - *gFallbackFonts.append() = tf->uniqueID(); + // add to appropriate fallback chains + FallbackFontRec fallbackRec; + fallbackRec.fFontID = tf->uniqueID(); + fallbackRec.fVariant = gSystemFonts[i].fVariant; + addFallbackFontLocked(fallbackRec, gSystemFonts[i].fLanguage); } firstInFamily = tf; @@ -609,6 +803,7 @@ static void initSystemFontsLocked() { } } } + finaliseFallbackFontListsLocked(); // do this after all fonts are loaded. This is our default font, and it // acts as a sentinel so we only execute loadSystemFontsLocked() once @@ -617,100 +812,19 @@ static void initSystemFontsLocked() { SkDEBUGCODE(dumpGlobalsLocked()); } -static SkFontID findUniqueIDLocked(const char* filename) { - // uniqueID is the index, offset by one, of the associated element in - // gSystemFonts[] (assumes system fonts are loaded before external fonts) - // return 0 if not found - for (int i = 0; i < gSystemFonts.count(); i++) { - if (strcmp(gSystemFonts[i].fFileName, filename) == 0) { - return i + 1; // assume unique id of i'th system font is i + 1 - } - } - return 0; -} - -static int findFallbackFontIndex(SkFontID fontId) { - for (int i = 0; i < gFallbackFonts.count(); i++) { - if (gFallbackFonts[i] == fontId) { +static int findFallbackFontIndex(SkFontID fontId, FallbackFontList* currentFallbackList) { + for (int i = 0; i < currentFallbackList->fList.count(); i++) { + if (currentFallbackList->fList[i].fFontID == fontId) { return i; } } return -1; } -static void reloadFallbackFontsLocked() { - SkGraphics::PurgeFontCache(); - - SkTDArray<FontFamily*> fallbackFamilies; - getFallbackFontFamilies(fallbackFamilies); - - gFallbackFonts.reset(); - - for (int i = 0; i < fallbackFamilies.count(); ++i) { - FontFamily *family = fallbackFamilies[i]; - - for (int j = 0; j < family->fFileNames.count(); ++j) { - const char* filename = family->fFileNames[j]; - if (filename) { - if (!haveSystemFont(filename)) { - SkDebugf("---- skipping fallback font %s because it was not " - "previously loaded as a system font", filename); - continue; - } - - // ensure the fallback font exists before adding it to the list - bool isFixedWidth; - SkString name; - SkTypeface::Style style; - if (!getNameAndStyle(filename, &name, &style, - &isFixedWidth, false)) { - continue; - } - - SkFontID uniqueID = findUniqueIDLocked(filename); - SkASSERT(uniqueID != 0); - if (findFallbackFontIndex(uniqueID) >= 0) { - SkDebugf("---- system font and fallback font files specify a duplicate " - "font %s, skipping the second occurrence", filename); - continue; - } - - SkDEBUGF(("---- reload %s as fallback[%d] fontID %d\n", - filename, gFallbackFonts.count(), uniqueID)); - - *gFallbackFonts.append() = uniqueID; - break; // The fallback set contains only the first font of each family - } - } - } - - fallbackFamilies.deleteAll(); -} - static void loadSystemFontsLocked() { -#if !defined(SK_BUILD_FOR_ANDROID_NDK) - static char prevLanguage[3]; - static char prevRegion[3]; - char language[3] = ""; - char region[3] = ""; - - getLocale(language, region); - if (!gDefaultNormal) { - strncpy(prevLanguage, language, 2); - strncpy(prevRegion, region, 2); initSystemFontsLocked(); - } else if (strncmp(language, prevLanguage, 2) || strncmp(region, prevRegion, 2)) { - strncpy(prevLanguage, language, 2); - strncpy(prevRegion, region, 2); - reloadFallbackFontsLocked(); } -#else - if (!gDefaultNormal) { - initSystemFontsLocked(); - reloadFallbackFontsLocked(); - } -#endif } /////////////////////////////////////////////////////////////////////////////// @@ -883,16 +997,20 @@ 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); + + FallbackFontList* currentFallbackList = + getFallbackFontListLocked(rec.fLanguage); + SkASSERT(currentFallbackList); SkASSERT(origTypeface != 0); SkASSERT(currTypeface != 0); @@ -906,19 +1024,32 @@ static SkFontID nextLogicalFontLocked(SkFontID currFontID, SkFontID origFontID) in our list. Note: list is zero-terminated, and returning zero means we have no more fonts to use for fallbacks. */ - int plainFallbackFontIndex = findFallbackFontIndex(plainFontID); + int plainFallbackFontIndex = findFallbackFontIndex(plainFontID, currentFallbackList); int nextFallbackFontIndex = plainFallbackFontIndex + 1; - 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 < currentFallbackList->fList.count()) { + bool normalFont = + (currentFallbackList->fList[nextFallbackFontIndex].fVariant == SkPaint::kDefault_Variant); + bool fontChosen = (currentFallbackList->fList[nextFallbackFontIndex].fVariant == recPreference); + if (normalFont || fontChosen) { + const SkTypeface* nextTypeface = + findFromUniqueIDLocked(currentFallbackList->fList[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; } @@ -958,3 +1089,143 @@ SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { stream->unref(); return face; } + +/////////////////////////////////////////////////////////////////////////////// +// Function from SkTypeface_android.h +/////////////////////////////////////////////////////////////////////////////// + +static SkFontID findFontIDForChar(SkUnichar uni, SkTypeface::Style style, + SkPaint::FontVariant fontVariant) { + SkTypeface* face = FindBestFace(gDefaultFamily, style); + if (!face) { + return 0; + } + + SkPaint paint; + paint.setTypeface(face); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + paint.setFontVariant(fontVariant); + + SkAutoGlyphCache autoCache(paint, NULL); + SkGlyphCache* cache = autoCache.getCache(); + SkFontID fontID = 0; + + SkScalerContext* ctx = cache->getScalerContext(); + if (ctx) { + return ctx->findTypefaceIdForChar(uni); + } + return 0; +} + +struct HB_UnicodeMapping { + HB_Script script; + 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_Armenian, 0x0531}, + {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_Sinhala, 0x0D90}, + {HB_Script_Thai, 0x0E01}, + {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" +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; +} + +struct TypefaceLookupStruct { + HB_Script script; + SkTypeface::Style style; + SkPaint::FontVariant fontVariant; + SkTypeface* typeface; +}; + +SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex); // This is the mutex for gTypefaceTable +static SkTDArray<TypefaceLookupStruct> gTypefaceTable; // This is protected by gTypefaceTableMutex + +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; + } + return 0; +} + +SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style, + SkPaint::FontVariant fontVariant) { + SkTypeface* retTypeface = NULL; + + SkAutoMutexAcquire ac(gTypefaceTableMutex); // Note: NOT gFamilyHeadAndNameListMutex + TypefaceLookupStruct key; + key.script = script; + key.style = style; + key.fontVariant = fontVariant; + int index = SkTSearch<TypefaceLookupStruct>( + (const TypefaceLookupStruct*) gTypefaceTable.begin(), + gTypefaceTable.count(), key, sizeof(TypefaceLookupStruct), + &typefaceLookupCompare); + if (index >= 0) { + retTypeface = gTypefaceTable[index].typeface; + } + else { + SkUnichar unichar = getUnicodeFromHBScript(script); + if (!unichar) { + return NULL; + } + SkFontID newFontID = findFontIDForChar(unichar, style, fontVariant); + // retrieve the typeface that corresponds to this fontID + retTypeface = FindFromUniqueID(newFontID); + 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; +} |