diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | ThirdPartyProject.prop | 9 | ||||
-rw-r--r-- | include/core/SkFontHost.h | 9 | ||||
-rw-r--r-- | include/core/SkPaint.h | 24 | ||||
-rw-r--r-- | include/core/SkScalerContext.h | 18 | ||||
-rw-r--r-- | include/ports/SkTypeface_android.h | 31 | ||||
-rw-r--r-- | src/core/SkGlyphCache.h | 2 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 16 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 8 | ||||
-rw-r--r-- | src/images/SkImageDecoder_libwebp.cpp | 100 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.cpp | 53 | ||||
-rw-r--r-- | src/ports/FontHostConfiguration_android.h | 17 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 4 | ||||
-rw-r--r-- | src/ports/SkFontHost_android.cpp | 231 |
14 files changed, 396 insertions, 127 deletions
@@ -295,6 +295,7 @@ LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/include/config \ $(LOCAL_PATH)/include/effects \ $(LOCAL_PATH)/include/images \ + $(LOCAL_PATH)/include/ports \ $(LOCAL_PATH)/include/utils \ $(LOCAL_PATH)/include/xml \ external/freetype/include \ diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop deleted file mode 100644 index 6bd7584..0000000 --- a/ThirdPartyProject.prop +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright 2010 Google Inc. All Rights Reserved. -#Fri Jul 16 10:03:09 PDT 2010 -currentVersion=Unknown -version=Unknown -isNative=true -name=skia -keywords=skia -onDevice=true -homepage=http\://code.google.com/p/skia/ diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h index 25c9ecb..ace08d8 100644 --- a/include/core/SkFontHost.h +++ b/include/core/SkFontHost.h @@ -154,6 +154,15 @@ public: */ static SkFontID NextLogicalFont(SkFontID currFontID, SkFontID origFontID); +#ifdef SK_BUILD_FOR_ANDROID + /* + * This Android-only version of NextLogicalFont allows us to pass in an + * entire Rec structure so that a caller can change fallback behavior + */ + static SkFontID NextLogicalFont(const SkScalerContext::Rec& rec); +#endif + + /////////////////////////////////////////////////////////////////////////// /** Given a filled-out rec, the fonthost may decide to modify it to reflect diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 0ec6698..1715013 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -665,6 +665,25 @@ public: @param locale set the paint's locale value for drawing text. */ void setTextLocale(const SkString& locale); + + + enum FontVariant { + kDefault_Variant, // Currently setting yourself to Default gives you Compact Variant + kCompact_Variant, + kElegant_Variant, + kLast_Variant = kElegant_Variant, + }; + + /** Return the font variant + @return the font variant used by this paint object + */ + FontVariant getFontVariant() const { return fFontVariant; } + + + /** Set the font variant + @param fontVariant set the paint's font variant for choosing fonts + */ + void setFontVariant(FontVariant fontVariant); #endif /** Return the paint's text size. @@ -861,10 +880,6 @@ public: SkPath* path) const; #ifdef SK_BUILD_FOR_ANDROID - - enum FontVariant { - kElegant_Variant, - }; const SkGlyph& getUnicharMetrics(SkUnichar); const SkGlyph& getGlyphMetrics(uint16_t); const void* findImage(const SkGlyph&); @@ -910,6 +925,7 @@ private: unsigned fHinting : 2; #ifdef SK_BUILD_FOR_ANDROID SkString fTextLocale; + FontVariant fFontVariant; #endif SkDrawCacheProc getDrawCacheProc() const; diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h index 29679d6..33c3c3d 100644 --- a/include/core/SkScalerContext.h +++ b/include/core/SkScalerContext.h @@ -15,6 +15,7 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkPoint.h" +#include "SkTypeface.h" //#define SK_USE_COLOR_LUMINANCE @@ -209,6 +210,9 @@ public: #ifdef SK_USE_COLOR_LUMINANCE uint32_t fLumBits; #endif +#ifdef SK_BUILD_FOR_ANDROID + SkPaint::FontVariant fFontVariant; +#endif uint8_t fMaskFormat; uint8_t fStrokeJoin; uint16_t fFlags; @@ -233,7 +237,6 @@ public: SkMask::Format getFormat() const { return static_cast<SkMask::Format>(fMaskFormat); } - #ifdef SK_USE_COLOR_LUMINANCE SkColor getLuminanceColor() const { return fLumBits; @@ -299,6 +302,19 @@ public: SkPaint::FontMetrics* mY); #ifdef SK_BUILD_FOR_ANDROID + // This function must be public for SkTypeface_android.h, but should not be + // called by other callers + SkFontID findTypefaceIdForChar(SkUnichar uni) { + SkScalerContext* ctx = this; + while (NULL != ctx) { + if (ctx->generateCharToGlyph(uni)) { + return ctx->fRec.fFontID; + } + ctx = ctx->getNextContext(); + } + return 0; + } + unsigned getBaseGlyphCount(SkUnichar charCode); #endif diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h index 0023879..c3eb3d1 100644 --- a/include/ports/SkTypeface_android.h +++ b/include/ports/SkTypeface_android.h @@ -11,28 +11,19 @@ #include "SkTypeface.h" #include "SkPaint.h" -#include "../harfbuzz/src/harfbuzz-shaper.h" - -enum FallbackScripts { - kArabic_FallbackScript, - kArmenian_FallbackScript, - kBengali_FallbackScript, - kDevanagari_FallbackScript, - kEthiopic_FallbackScript, - kGeorgian_FallbackScript, - kHebrewRegular_FallbackScript, - kHebrewBold_FallbackScript, - kKannada_FallbackScript, - kMalayalam_FallbackScript, - kTamilRegular_FallbackScript, - kTamilBold_FallbackScript, - kThai_FallbackScript, - kTelugu_FallbackScript, - kFallbackScriptNumber -}; +#include "../harfbuzz/src/harfbuzz-shaper.h" +/** + * Return a new typeface for a fallback script. If the script is + * not valid, or can not map to a font, returns null. + * @param script The harfbuzz script id. + * @param style The font style, for example bold + * @param elegant true if we want the web friendly elegant version of the font + * @return reference to the matching typeface. Caller must call + * unref() when they are done. + */ SK_API SkTypeface* SkCreateTypefaceForScript(HB_Script script, SkTypeface::Style style, - SkPaint::FontVariant fontVariant) { return NULL; } + SkPaint::FontVariant fontVariant = SkPaint::kDefault_Variant); #endif 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/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/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<SkBitmap> 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; } diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp index d1164c8..9296769 100644 --- a/src/ports/FontHostConfiguration_android.cpp +++ b/src/ports/FontHostConfiguration_android.cpp @@ -18,6 +18,7 @@ #include "FontHostConfiguration_android.h" #include "SkString.h" #include "SkTDArray.h" +#include "SkTypeface.h" #include <expat.h> #if !defined(SK_BUILD_FOR_ANDROID_NDK) #include <cutils/properties.h> @@ -46,12 +47,13 @@ struct FamilyData { XML_Parser *parser; // The expat parser doing the work SkTDArray<FontFamily*> &families; // The array that each family is put into as it is parsed FontFamily *currentFamily; // The current family being created + FontFileInfo *currentFontInfo; // The current fontInfo being created int currentTag; // A flag to indicate whether we're in nameset/fileset tags }; /** * Handler for arbitrary text. This is used to parse the text inside each name - * or file tag. The resulting strings are put into the fNames or fFileNames arrays. + * or file tag. The resulting strings are put into the fNames or FontFileInfo arrays. */ void textHandler(void *data, const char *s, int len) { FamilyData *familyData = (FamilyData*) data; @@ -68,7 +70,9 @@ void textHandler(void *data, const char *s, int len) { *(familyData->currentFamily->fNames.append()) = buff; break; case FILESET_TAG: - *(familyData->currentFamily->fFileNames.append()) = buff; + if (familyData->currentFontInfo) { + familyData->currentFontInfo->fFileName = buff; + } break; default: // Noop - don't care about any text that's not in the Fonts or Names list @@ -78,6 +82,39 @@ void textHandler(void *data, const char *s, int len) { } /** + * Handler for font files. This processes the attributes for language and variants + * then lets textHandler handle the actual file name + */ +void fontFileElementHandler(FamilyData *familyData, const char **attributes) { + FontFileInfo* newFileInfo = new FontFileInfo(); + if (attributes) { + int currentAttributeIndex = 0; + while (attributes[currentAttributeIndex]) { + const char* attributeName = attributes[currentAttributeIndex]; + const char* attributeValue = attributes[currentAttributeIndex+1]; + int nameLength = strlen(attributeName); + int valueLength = strlen(attributeValue); + if (strncmp(attributeName, "variant", nameLength) == 0) { + if (strncmp(attributeValue, "elegant", valueLength) == 0) { + newFileInfo->fVariant = SkPaint::kElegant_Variant; + } else if (strncmp(attributeValue, "compact", valueLength) == 0) { + newFileInfo->fVariant = SkPaint::kCompact_Variant; + } + } else if (strncmp(attributeName, "language", nameLength) == 0) { + if (strncmp(attributeValue, "ja", valueLength) == 0) { + newFileInfo->fLanguage = "ja"; + } //else if (other languages) + } + //each element is a pair of attributeName/attributeValue string pairs + currentAttributeIndex += 2; + } + } + *(familyData->currentFamily->fFontFileArray.append()) = newFileInfo; + familyData->currentFontInfo = newFileInfo; + XML_SetCharacterDataHandler(*familyData->parser, textHandler); +} + +/** * Handler for the start of a tag. The only tags we expect are family, nameset, * fileset, name, and file. */ @@ -98,14 +135,16 @@ void startElementHandler(void *data, const char *tag, const char **atts) { familyData->currentFamily->order = value; } } - } else if (len == 7 && strncmp(tag, "nameset", len)== 0) { + } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { familyData->currentTag = NAMESET_TAG; } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { familyData->currentTag = FILESET_TAG; - } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || - (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { + } else if (strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) { // If it's a Name, parse the text inside XML_SetCharacterDataHandler(*familyData->parser, textHandler); + } else if (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG) { + // If it's a file, parse the attributes, then parse the text inside + fontFileElementHandler(familyData, atts); } } @@ -120,9 +159,9 @@ void endElementHandler(void *data, const char *tag) { // Done parsing a Family - store the created currentFamily in the families array *familyData->families.append() = familyData->currentFamily; familyData->currentFamily = NULL; - } else if (len == 7 && strncmp(tag, "nameset", len)== 0) { + } else if (len == 7 && strncmp(tag, "nameset", len) == 0) { familyData->currentTag = NO_TAG; - } else if (len == 7 && strncmp(tag, "fileset", len)== 0) { + } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { familyData->currentTag = NO_TAG; } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h index 2441f0e..c8a70e3 100644 --- a/src/ports/FontHostConfiguration_android.h +++ b/src/ports/FontHostConfiguration_android.h @@ -18,18 +18,27 @@ #define FONTHOSTCONFIGURATION_ANDROID_H_ #include "SkTDArray.h" +#include "SkPaint.h" + +struct FontFileInfo { + FontFileInfo() : fVariant(SkPaint::kDefault_Variant), fLanguage(NULL), fFileName(NULL) {} + const char* fFileName; + SkPaint::FontVariant fVariant; + const char* fLanguage; // We may eventually use a enum for this +}; + /** * The FontFamily data structure is created during parsing and handed back to * Skia to fold into its representation of font families. fNames is the list of - * font names that alias to a font family. fFileNames is the list of font - * filenames for the family. Order is the priority order for the font. This is + * font names that alias to a font family. fontFileArray is the list of information + * about each file. Order is the priority order for the font. This is * used internally to determine the order in which to place fallback fonts as * they are read from the configuration files. */ struct FontFamily { - SkTDArray<const char*> fNames; - SkTDArray<const char*> fFileNames; + SkTDArray<const char*> fNames; + SkTDArray<FontFileInfo*> fFontFileArray; int order; }; diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index fc60ef9..6aa2120 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -961,8 +961,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; } } diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 2c58079..38d1d06 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -27,6 +27,9 @@ #include "FontHostConfiguration_android.h" #include <stdio.h> #include <string.h> +#include "SkGlyphCache.h" +#include "SkTypeface_android.h" +#include "SkTSearch.h" //#define SkDEBUGF(args ) SkDebugf args @@ -73,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); /////////////////////////////////////////////////////////////////////////////// @@ -147,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) { @@ -177,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 */ @@ -394,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 @@ -407,7 +431,7 @@ static const char* gFBNames[] = { NULL }; null for the list. The names list must be NULL-terminated. */ static SkTDArray<FontInitRec> gSystemFonts; -static SkTDArray<SkFontID> gFallbackFonts; +static SkTDArray<FallbackFontRec> gFallbackFonts; // these globals are assigned (once) by loadSystemFontsLocked() static FamilyRec* gDefaultFamily = NULL; @@ -490,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); @@ -500,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 @@ -592,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; @@ -631,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; } } @@ -649,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 " @@ -677,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 } } @@ -883,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); @@ -908,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; } @@ -958,3 +1001,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; +} |