aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/SkCanvas.cpp3
-rw-r--r--src/core/SkGlyphCache.h2
-rw-r--r--src/core/SkLanguage.cpp54
-rw-r--r--src/core/SkPaint.cpp42
-rw-r--r--src/core/SkPath.cpp7
-rw-r--r--src/core/SkScalerContext.cpp66
-rw-r--r--src/effects/SkGradientShader.cpp4
-rw-r--r--src/images/SkImageDecoder_libjpeg.cpp2
-rw-r--r--src/images/SkImageDecoder_libwebp.cpp120
-rw-r--r--src/ports/FontHostConfiguration_android.cpp116
-rw-r--r--src/ports/FontHostConfiguration_android.h27
-rw-r--r--src/ports/SkFontHost_FreeType.cpp64
-rw-r--r--src/ports/SkFontHost_android.cpp499
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;
+}