diff options
-rw-r--r-- | gm/androidfallback.cpp | 87 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/ports/SkTypeface_android.h | 16 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 122 | ||||
-rw-r--r-- | src/ports/SkFontConfigInterface_android.cpp | 66 |
5 files changed, 292 insertions, 0 deletions
diff --git a/gm/androidfallback.cpp b/gm/androidfallback.cpp new file mode 100644 index 0000000000..3ef8f1ca51 --- /dev/null +++ b/gm/androidfallback.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" + +namespace skiagm { + +class AndroidFallbackGM : public GM { +public: + AndroidFallbackGM() { + this->setBGColor(0xFFCCCCCC); + } + +protected: + virtual SkString onShortName() SK_OVERRIDE { + return SkString("android_paint"); + } + + virtual SkISize onISize() SK_OVERRIDE { + return make_isize(500, 500); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + + SkPaint paint; + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + paint.setTextSize(24); + +#if SK_BUILD_FOR_ANDROID + SkPaintOptionsAndroid options = paint.getPaintOptionsAndroid(); + options.setUseFontFallbacks(true); + paint.setPaintOptionsAndroid(options); +#endif + + // "ื foo ๅ
่ bar เค" + const uint16_t unicodeStr[] = {0x05D0, 0x0020, 0x0066, 0x006F, 0x006F, 0x0020, 0x514D, + 0x820c, 0x0020, 0x0062, 0x0061, 0x0072, 0x0020, 0x0915}; + const int strLength = sizeof(unicodeStr) / sizeof(uint16_t); + const int strByteLength = sizeof(unicodeStr); + + SkScalar posX[strLength]; + SkPoint posXY[strLength]; + + for (int i = 0; i < strLength; ++i) { + posX[i] = SkIntToScalar(i * 24); + posXY[i].fX = posX[i]; + posXY[i].fY = SkIntToScalar(24 + i); + } + + canvas->translate(SkIntToScalar(10), SkIntToScalar(25)); + // This currently causes the PDF backend to assert + // canvas->drawText(unicodeStr, strByteLength, 0, 0, paint); + + canvas->translate(0, SkIntToScalar(75)); + canvas->drawPosTextH(unicodeStr, strByteLength, posX, 0, paint); + +#if SK_BUILD_FOR_ANDROID + options.setLanguage("ja"); + paint.setPaintOptionsAndroid(options); +#endif + + canvas->translate(0, SkIntToScalar(75)); + canvas->drawPosText(unicodeStr, strByteLength, posXY, paint); + + SkPath path; + path.moveTo(0, 0); + path.quadTo(50.0f, 100.0f, 250.0f, 150.0f); + + canvas->translate(0, SkIntToScalar(75)); + canvas->drawTextOnPath(unicodeStr, strByteLength, path, NULL, paint); + } + +private: + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +#if SK_BUILD_FOR_ANDROID +DEF_GM( return SkNEW(AndroidFallbackGM); ) +#endif + +} diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index bace62fd75..5e2dfc17e1 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -4,6 +4,7 @@ '../gm/aaclip.cpp', '../gm/aarectmodes.cpp', '../gm/alphagradients.cpp', + '../gm/androidfallback.cpp', '../gm/arcofzorro.cpp', '../gm/arithmode.cpp', '../gm/beziereffects.cpp', diff --git a/include/ports/SkTypeface_android.h b/include/ports/SkTypeface_android.h index 1ee923c4e6..2166e08394 100644 --- a/include/ports/SkTypeface_android.h +++ b/include/ports/SkTypeface_android.h @@ -68,6 +68,22 @@ SK_API void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackco SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontID, const SkPaintOptionsAndroid& options); +/** + * Given a glyphID (built using fallback font chaining) and its origin typeface + * return the actual typeface within the fallback chain that this glyphID + * resolves to. If no suitable typeface is found then NULL is returned. However, + * if returned typeface is not NULL it is assumed to be globally cached so the + * caller need not ref it. + * + * Optionally, if lower/upper bound params are provided and the returned + * typeface is not NULL, then these params are populated with the range of + * glyphIDs that this typeface is capable of resolving. The lower bound is + * inclusive while the upper bound is exclusive. + */ +SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface, + const SkPaintOptionsAndroid& options, + int* lowerBounds = NULL, int* upperBounds = NULL); + #endif // #ifdef SK_BUILD_FOR_ANDROID #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index d13d30cbfb..e4b07f04fa 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -33,6 +33,22 @@ #include "SkTypefacePriv.h" #include "SkTSet.h" +#ifdef SK_BUILD_FOR_ANDROID +#include "SkTypeface_android.h" + +struct TypefaceFallbackData { + SkTypeface* typeface; + int lowerBounds; + int upperBounds; + + bool operator==(const TypefaceFallbackData& b) const { + return typeface == b.typeface && + lowerBounds == b.lowerBounds && + upperBounds == b.upperBounds; + } +}; +#endif + // Utility functions static void emit_pdf_color(SkColor color, SkWStream* result) { @@ -1115,6 +1131,112 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len, return; } +#ifdef SK_BUILD_FOR_ANDROID + /* + * In the case that we have enabled fallback fonts on Android we need to + * take the following steps to ensure that the PDF draws all characters, + * regardless of their underlying font file, correctly. + * + * 1. Convert input into GlyphID encoding if it currently is not + * 2. Iterate over the glyphIDs and identify the actual typeface that each + * glyph resolves to + * 3. Iterate over those typefaces and recursively call this function with + * only the glyphs (and their positions) that the typeface is capable of + * resolving. + */ + if (paint.getPaintOptionsAndroid().isUsingFontFallbacks()) { + uint16_t* glyphIDs = NULL; + SkGlyphStorage tmpStorage(0); + size_t numGlyphs = 0; + + // convert to glyphIDs + if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { + numGlyphs = len / 2; + glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>(text)); + } else { + numGlyphs = paint.textToGlyphs(text, len, NULL); + tmpStorage.reset(numGlyphs); + paint.textToGlyphs(text, len, tmpStorage.get()); + glyphIDs = tmpStorage.get(); + } + + // if no typeface is provided in the paint get the default + SkAutoTUnref<SkTypeface> origFace(SkSafeRef(paint.getTypeface())); + if (NULL == origFace.get()) { + origFace.reset(SkTypeface::RefDefault()); + } + const uint16_t origGlyphCount = origFace->countGlyphs(); + + // keep a list of the already visited typefaces and some data about them + SkTDArray<TypefaceFallbackData> visitedTypefaces; + + // find all the typefaces needed to resolve this run of text + bool usesOriginalTypeface = false; + for (uint16_t x = 0; x < numGlyphs; ++x) { + // optimization that checks to see if original typeface can resolve the glyph + if (glyphIDs[x] < origGlyphCount) { + usesOriginalTypeface = true; + continue; + } + + // find the fallback typeface that supports this glyph + TypefaceFallbackData data; + data.typeface = SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(), + paint.getPaintOptionsAndroid(), + &data.lowerBounds, &data.upperBounds); + // add the typeface and its data if we don't have it + if (data.typeface && !visitedTypefaces.contains(data)) { + visitedTypefaces.push(data); + } + } + + // if the original font was used then add it to the list as well + if (usesOriginalTypeface) { + TypefaceFallbackData* data = visitedTypefaces.push(); + data->typeface = origFace.get(); + data->lowerBounds = 0; + data->upperBounds = origGlyphCount; + } + + // keep a scratch glyph and pos storage + SkAutoTMalloc<SkScalar> posStorage(len * scalarsPerPos); + SkScalar* tmpPos = posStorage.get(); + SkGlyphStorage glyphStorage(numGlyphs); + uint16_t* tmpGlyphIDs = glyphStorage.get(); + + // loop through all the valid typefaces, trim the glyphs to only those + // resolved by the typeface, and then draw that run of glyphs + for (int x = 0; x < visitedTypefaces.count(); ++x) { + const TypefaceFallbackData& data = visitedTypefaces[x]; + + int tmpGlyphCount = 0; + for (uint16_t y = 0; y < numGlyphs; ++y) { + if (glyphIDs[y] >= data.lowerBounds && glyphIDs[y] < data.upperBounds) { + tmpGlyphIDs[tmpGlyphCount] = glyphIDs[y] - data.lowerBounds; + memcpy(&(tmpPos[tmpGlyphCount * scalarsPerPos]), + &(pos[y * scalarsPerPos]), + scalarsPerPos * sizeof(SkScalar)); + tmpGlyphCount++; + } + } + + // recursively call this function with the right typeface + SkPaint tmpPaint = paint; + tmpPaint.setTypeface(data.typeface); + tmpPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + // turn off fallback chaining + SkPaintOptionsAndroid paintOpts = tmpPaint.getPaintOptionsAndroid(); + paintOpts.setUseFontFallbacks(false); + tmpPaint.setPaintOptionsAndroid(paintOpts); + + this->drawPosText(d, tmpGlyphIDs, tmpGlyphCount * 2, tmpPos, constY, + scalarsPerPos, tmpPaint); + } + return; + } +#endif + SkGlyphStorage storage(0); uint16_t* glyphIDs = NULL; size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage, diff --git a/src/ports/SkFontConfigInterface_android.cpp b/src/ports/SkFontConfigInterface_android.cpp index 7382d00b45..9c188d90c6 100644 --- a/src/ports/SkFontConfigInterface_android.cpp +++ b/src/ports/SkFontConfigInterface_android.cpp @@ -104,6 +104,9 @@ public: SkPaintOptionsAndroid::FontVariant fontVariant); SkTypeface* nextLogicalTypeface(SkFontID currFontID, SkFontID origFontID, const SkPaintOptionsAndroid& options); + SkTypeface* getTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface, + const SkPaintOptionsAndroid& options, + int* lowerBounds, int* upperBounds); private: void addFallbackFamily(FamilyRecID fontRecID); @@ -672,6 +675,61 @@ SkTypeface* SkFontConfigInterfaceAndroid::nextLogicalTypeface(SkFontID currFontI return SkSafeRef(nextLogicalTypeface); } +SkTypeface* SkFontConfigInterfaceAndroid::getTypefaceForGlyphID(uint16_t glyphID, + const SkTypeface* origTypeface, + const SkPaintOptionsAndroid& opts, + int* lBounds, int* uBounds) { + // If we aren't using fallbacks then we shouldn't be calling this + SkASSERT(opts.isUsingFontFallbacks()); + SkASSERT(origTypeface); + + SkTypeface* currentTypeface = NULL; + int lowerBounds = 0; //inclusive + int upperBounds = origTypeface->countGlyphs(); //exclusive + + // check to see if the glyph is in the bounds of the origTypeface + if (glyphID < upperBounds) { + currentTypeface = const_cast<SkTypeface*>(origTypeface); + } else { + FallbackFontList* currentFallbackList = findFallbackFontList(opts.getLanguage()); + SkASSERT(currentFallbackList); + + // If an object is set to prefer "kDefault_Variant" it means they have no preference + // In this case, we set the value to "kCompact_Variant" + SkPaintOptionsAndroid::FontVariant variant = opts.getFontVariant(); + if (variant == SkPaintOptionsAndroid::kDefault_Variant) { + variant = SkPaintOptionsAndroid::kCompact_Variant; + } + + int32_t acceptedVariants = SkPaintOptionsAndroid::kDefault_Variant | variant; + SkTypeface::Style origStyle = origTypeface->style(); + + for (int x = 0; x < currentFallbackList->count(); ++x) { + const FamilyRecID familyRecID = currentFallbackList->getAt(x); + const SkPaintOptionsAndroid& familyOptions = fFontFamilies[familyRecID].fPaintOptions; + if ((familyOptions.getFontVariant() & acceptedVariants) != 0) { + FontRecID matchedFont = find_best_style(fFontFamilies[familyRecID], origStyle); + currentTypeface = this->getTypefaceForFontRec(matchedFont); + lowerBounds = upperBounds; + upperBounds += currentTypeface->countGlyphs(); + if (glyphID < upperBounds) { + break; + } + } + } + } + + if (NULL != currentTypeface) { + if (lBounds) { + *lBounds = lowerBounds; + } + if (uBounds) { + *uBounds = upperBounds; + } + } + return currentTypeface; +} + /////////////////////////////////////////////////////////////////////////////// bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) { @@ -704,6 +762,14 @@ SkTypeface* SkAndroidNextLogicalTypeface(SkFontID currFontID, SkFontID origFontI } +SkTypeface* SkGetTypefaceForGlyphID(uint16_t glyphID, const SkTypeface* origTypeface, + const SkPaintOptionsAndroid& options, + int* lowerBounds, int* upperBounds) { + SkFontConfigInterfaceAndroid* fontConfig = getSingletonInterface(); + return fontConfig->getTypefaceForGlyphID(glyphID, origTypeface, options, + lowerBounds, upperBounds); +} + /////////////////////////////////////////////////////////////////////////////// #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK |