Loading core/jni/android/graphics/TextLayoutCache.cpp +305 −0 Original line number Diff line number Diff line Loading @@ -215,4 +215,309 @@ void TextLayoutCache::dumpCacheStats() { LOGD("------------------------------------------------"); } /** * TextLayoutCacheKey */ TextLayoutCacheKey::TextLayoutCacheKey() : text(NULL), start(0), count(0), contextCount(0), dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), hinting(SkPaint::kNo_Hinting) { } TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count, size_t contextCount, int dirFlags) : text(text), start(start), count(count), contextCount(contextCount), dirFlags(dirFlags) { typeface = paint->getTypeface(); textSize = paint->getTextSize(); textSkewX = paint->getTextSkewX(); textScaleX = paint->getTextScaleX(); flags = paint->getFlags(); hinting = paint->getHinting(); } bool TextLayoutCacheKey::operator<(const TextLayoutCacheKey& rhs) const { LTE_INT(count) { LTE_INT(contextCount) { LTE_INT(start) { LTE_INT(typeface) { LTE_FLOAT(textSize) { LTE_FLOAT(textSkewX) { LTE_FLOAT(textScaleX) { LTE_INT(flags) { LTE_INT(hinting) { LTE_INT(dirFlags) { return strncmp16(text, rhs.text, contextCount) < 0; } } } } } } } } } } return false; } void TextLayoutCacheKey::internalTextCopy() { textCopy.setTo(text, contextCount); text = textCopy.string(); } size_t TextLayoutCacheKey::getSize() { return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; } /** * TextLayoutCacheValue */ TextLayoutCacheValue::TextLayoutCacheValue() { advances = NULL; totalAdvance = 0; } TextLayoutCacheValue::~TextLayoutCacheValue() { delete[] advances; } void TextLayoutCacheValue::setElapsedTime(uint32_t time) { elapsedTime = time; } uint32_t TextLayoutCacheValue::getElapsedTime() { return elapsedTime; } void TextLayoutCacheValue::computeAdvances(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { advances = new float[count]; this->count = count; #if RTL_USE_HARFBUZZ computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #else computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #endif #if DEBUG_ADVANCES LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - " "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, totalAdvance, advances[0], advances[1], advances[2], advances[3]); #endif } void TextLayoutCacheValue::copyResult(jfloat* outAdvances, jfloat* outTotalAdvance) { memcpy(outAdvances, advances, count * sizeof(jfloat)); *outTotalAdvance = totalAdvance; } size_t TextLayoutCacheValue::getSize() { return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * count; } void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { bool isRTL = dirFlags & 0x1; font->klass = &harfbuzzSkiaClass; font->userData = 0; // The values which harfbuzzSkiaClass returns are already scaled to // pixel units, so we just set all these to one to disable further // scaling. font->x_ppem = 1; font->y_ppem = 1; font->x_scale = 1; font->y_scale = 1; memset(shaperItem, 0, sizeof(*shaperItem)); shaperItem->font = font; shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable); shaperItem->kerning_applied = false; // We cannot know, ahead of time, how many glyphs a given script run // will produce. We take a guess that script runs will not produce more // than twice as many glyphs as there are code points plus a bit of // padding and fallback if we find that we are wrong. createGlyphArrays(shaperItem, (contextCount + 2) * 2); // Free memory for clusters if needed and recreate the clusters array if (shaperItem->log_clusters) { delete shaperItem->log_clusters; } shaperItem->log_clusters = new unsigned short[contextCount]; shaperItem->item.pos = start; shaperItem->item.length = count; shaperItem->item.bidiLevel = isRTL; shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common; shaperItem->string = chars; shaperItem->stringLength = contextCount; fontData->typeFace = paint->getTypeface(); fontData->textSize = paint->getTextSize(); fontData->textSkewX = paint->getTextSkewX(); fontData->textScaleX = paint->getTextScaleX(); fontData->flags = paint->getFlags(); fontData->hinting = paint->getHinting(); shaperItem->font->userData = fontData; } void TextLayoutCacheValue::shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { // Setup Harfbuzz Shaper setupShaperItem(shaperItem, font, fontData, paint, chars, start, count, contextCount, dirFlags); // Shape resetGlyphArrays(shaperItem); while (!HB_ShapeItem(shaperItem)) { // We overflowed our arrays. Resize and retry. // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. deleteGlyphArrays(shaperItem); createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1); resetGlyphArrays(shaperItem); } } void TextLayoutCacheValue::computeAdvancesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { bool isRTL = dirFlags & 0x1; HB_ShaperItem shaperItem; HB_FontRec font; FontData fontData; shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count, contextCount, dirFlags); #if DEBUG_ADVANCES LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, shaperItem.kerning_applied); LOGD(" -- string= '%s'", String8(chars, contextCount).string()); LOGD(" -- isDevKernText=%d", paint->isDevKernText()); #endif jfloat totalAdvance = 0; for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); #if DEBUG_ADVANCES LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], totalAdvance); #endif } deleteGlyphArrays(&shaperItem); HB_FreeFace(shaperItem.face); *outTotalAdvance = totalAdvance; } void TextLayoutCacheValue::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); jchar* buffer = tempBuffer.get(); SkScalar* scalarArray = (SkScalar*)outAdvances; // this is where we'd call harfbuzz // for now we just use ushape.c size_t widths; const jchar* text; if (dirFlags & 0x1) { // rtl, call arabic shaping in case UErrorCode status = U_ZERO_ERROR; // Use fixed length since we need to keep start and count valid u_shapeArabic(chars, contextCount, buffer, contextCount, U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); // we shouldn't fail unless there's an out of memory condition, // in which case we're hosed anyway for (int i = start, e = i + count; i < e; ++i) { if (buffer[i] == UNICODE_NOT_A_CHAR) { buffer[i] = UNICODE_ZWSP; // zero-width-space for skia } } text = buffer + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } else { text = chars + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } jfloat totalAdvance = 0; if (widths < count) { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", widths); #endif // Skia operates on code points, not code units, so surrogate pairs return only // one value. Expand the result so we have one value per UTF-16 code unit. // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, // leaving the remaining widths zero. Not nice. for (size_t i = 0, p = 0; i < widths; ++i) { totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); if (p < count && text[p] >= UNICODE_FIRST_LOW_SURROGATE && text[p] < UNICODE_FIRST_PRIVATE_USE && text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { outAdvances[p++] = 0; } #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } else { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", count); #endif for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } *outTotalAdvance = totalAdvance; } void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem* shaperItem) { delete[] shaperItem->glyphs; delete[] shaperItem->attributes; delete[] shaperItem->advances; delete[] shaperItem->offsets; } void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem* shaperItem, int size) { shaperItem->glyphs = new HB_Glyph[size]; shaperItem->attributes = new HB_GlyphAttributes[size]; shaperItem->advances = new HB_Fixed[size]; shaperItem->offsets = new HB_FixedPoint[size]; shaperItem->num_glyphs = size; } void TextLayoutCacheValue::resetGlyphArrays(HB_ShaperItem* shaperItem) { int size = shaperItem->num_glyphs; // All the types here don't have pointers. It is safe to reset to // zero unless Harfbuzz breaks the compatibility in the future. memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0])); memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0])); memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0])); memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0])); } } // namespace android core/jni/android/graphics/TextLayoutCache.h +25 −275 Original line number Diff line number Diff line Loading @@ -64,62 +64,24 @@ namespace android { */ class TextLayoutCacheKey { public: TextLayoutCacheKey() : text(NULL), start(0), count(0), contextCount(0), dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), hinting(SkPaint::kNo_Hinting) { } TextLayoutCacheKey(); TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count, size_t contextCount, int dirFlags) : text(text), start(start), count(count), contextCount(contextCount), dirFlags(dirFlags) { typeface = paint->getTypeface(); textSize = paint->getTextSize(); textSkewX = paint->getTextSkewX(); textScaleX = paint->getTextScaleX(); flags = paint->getFlags(); hinting = paint->getHinting(); } size_t contextCount, int dirFlags); bool operator<(const TextLayoutCacheKey& rhs) const { LTE_INT(count) { LTE_INT(contextCount) { LTE_INT(start) { LTE_INT(typeface) { LTE_FLOAT(textSize) { LTE_FLOAT(textSkewX) { LTE_FLOAT(textScaleX) { LTE_INT(flags) { LTE_INT(hinting) { LTE_INT(dirFlags) { return strncmp16(text, rhs.text, contextCount) < 0; } } } } } } } } } } return false; } bool operator<(const TextLayoutCacheKey& rhs) const; // We need to copy the text when we insert the key into the cache itself. // We don't need to copy the text when we are only comparing keys. void internalTextCopy() { textCopy.setTo(text, contextCount); text = textCopy.string(); } /** * We need to copy the text when we insert the key into the cache itself. * We don't need to copy the text when we are only comparing keys. */ void internalTextCopy(); /** * Get the size of the Cache key. */ size_t getSize() { return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; } size_t getSize(); private: const UChar* text; Loading @@ -137,232 +99,41 @@ private: }; // TextLayoutCacheKey /* * TextLayoutCacheEntry is the Cache entry * TextLayoutCacheValue is the Cache value */ class TextLayoutCacheValue { public: TextLayoutCacheValue() { advances = NULL; totalAdvance = 0; } ~TextLayoutCacheValue() { delete[] advances; } void setElapsedTime(uint32_t time) { elapsedTime = time; } TextLayoutCacheValue(); ~TextLayoutCacheValue(); uint32_t getElapsedTime() { return elapsedTime; } void setElapsedTime(uint32_t time); uint32_t getElapsedTime(); void computeAdvances(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { advances = new float[count]; this->count = count; #if RTL_USE_HARFBUZZ computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #else computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #endif #if DEBUG_ADVANCES LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - " "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, totalAdvance, advances[0], advances[1], advances[2], advances[3]); #endif } size_t contextCount, int dirFlags); void copyResult(jfloat* outAdvances, jfloat* outTotalAdvance) { memcpy(outAdvances, advances, count * sizeof(jfloat)); *outTotalAdvance = totalAdvance; } void copyResult(jfloat* outAdvances, jfloat* outTotalAdvance); /** * Get the size of the Cache entry */ size_t getSize() { return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * count; } size_t getSize(); static void setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { bool isRTL = dirFlags & 0x1; font->klass = &harfbuzzSkiaClass; font->userData = 0; // The values which harfbuzzSkiaClass returns are already scaled to // pixel units, so we just set all these to one to disable further // scaling. font->x_ppem = 1; font->y_ppem = 1; font->x_scale = 1; font->y_scale = 1; memset(shaperItem, 0, sizeof(*shaperItem)); shaperItem->font = font; shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable); shaperItem->kerning_applied = false; // We cannot know, ahead of time, how many glyphs a given script run // will produce. We take a guess that script runs will not produce more // than twice as many glyphs as there are code points plus a bit of // padding and fallback if we find that we are wrong. createGlyphArrays(shaperItem, (contextCount + 2) * 2); // Free memory for clusters if needed and recreate the clusters array if (shaperItem->log_clusters) { delete shaperItem->log_clusters; } shaperItem->log_clusters = new unsigned short[contextCount]; shaperItem->item.pos = start; shaperItem->item.length = count; shaperItem->item.bidiLevel = isRTL; shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common; shaperItem->string = chars; shaperItem->stringLength = contextCount; fontData->typeFace = paint->getTypeface(); fontData->textSize = paint->getTextSize(); fontData->textSkewX = paint->getTextSkewX(); fontData->textScaleX = paint->getTextScaleX(); fontData->flags = paint->getFlags(); fontData->hinting = paint->getHinting(); shaperItem->font->userData = fontData; } int dirFlags); static void shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { // Setup Harfbuzz Shaper setupShaperItem(shaperItem, font, fontData, paint, chars, start, count, contextCount, dirFlags); // Shape resetGlyphArrays(shaperItem); while (!HB_ShapeItem(shaperItem)) { // We overflowed our arrays. Resize and retry. // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. deleteGlyphArrays(shaperItem); createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1); resetGlyphArrays(shaperItem); } } int dirFlags); static void computeAdvancesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { bool isRTL = dirFlags & 0x1; HB_ShaperItem shaperItem; HB_FontRec font; FontData fontData; shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count, contextCount, dirFlags); #if DEBUG_ADVANCES LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, shaperItem.kerning_applied); LOGD(" -- string= '%s'", String8(chars, contextCount).string()); LOGD(" -- isDevKernText=%d", paint->isDevKernText()); #endif jfloat totalAdvance = 0; for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); #if DEBUG_ADVANCES LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], totalAdvance); #endif } deleteGlyphArrays(&shaperItem); HB_FreeFace(shaperItem.face); *outTotalAdvance = totalAdvance; } jfloat* outAdvances, jfloat* outTotalAdvance); static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); jchar* buffer = tempBuffer.get(); SkScalar* scalarArray = (SkScalar*)outAdvances; // this is where we'd call harfbuzz // for now we just use ushape.c size_t widths; const jchar* text; if (dirFlags & 0x1) { // rtl, call arabic shaping in case UErrorCode status = U_ZERO_ERROR; // Use fixed length since we need to keep start and count valid u_shapeArabic(chars, contextCount, buffer, contextCount, U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); // we shouldn't fail unless there's an out of memory condition, // in which case we're hosed anyway for (int i = start, e = i + count; i < e; ++i) { if (buffer[i] == UNICODE_NOT_A_CHAR) { buffer[i] = UNICODE_ZWSP; // zero-width-space for skia } } text = buffer + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } else { text = chars + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } jfloat totalAdvance = 0; if (widths < count) { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", widths); #endif // Skia operates on code points, not code units, so surrogate pairs return only // one value. Expand the result so we have one value per UTF-16 code unit. // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, // leaving the remaining widths zero. Not nice. for (size_t i = 0, p = 0; i < widths; ++i) { totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); if (p < count && text[p] >= UNICODE_FIRST_LOW_SURROGATE && text[p] < UNICODE_FIRST_PRIVATE_USE && text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { outAdvances[p++] = 0; } #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } else { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", count); #endif for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } *outTotalAdvance = totalAdvance; } jfloat* outAdvances, jfloat* outTotalAdvance); private: jfloat* advances; Loading @@ -371,32 +142,11 @@ private: uint32_t elapsedTime; static void deleteGlyphArrays(HB_ShaperItem* shaperItem) { delete[] shaperItem->glyphs; delete[] shaperItem->attributes; delete[] shaperItem->advances; delete[] shaperItem->offsets; } static void createGlyphArrays(HB_ShaperItem* shaperItem, int size) { shaperItem->glyphs = new HB_Glyph[size]; shaperItem->attributes = new HB_GlyphAttributes[size]; shaperItem->advances = new HB_Fixed[size]; shaperItem->offsets = new HB_FixedPoint[size]; shaperItem->num_glyphs = size; } static void resetGlyphArrays(HB_ShaperItem* shaperItem) { int size = shaperItem->num_glyphs; // All the types here don't have pointers. It is safe to reset to // zero unless Harfbuzz breaks the compatibility in the future. memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0])); memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0])); memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0])); memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0])); } static void deleteGlyphArrays(HB_ShaperItem* shaperItem); static void createGlyphArrays(HB_ShaperItem* shaperItem, int size); static void resetGlyphArrays(HB_ShaperItem* shaperItem); }; // TextLayoutCacheEntry }; // TextLayoutCacheValue class TextLayoutCache: public OnEntryRemoved<TextLayoutCacheKey, TextLayoutCacheValue*> Loading Loading
core/jni/android/graphics/TextLayoutCache.cpp +305 −0 Original line number Diff line number Diff line Loading @@ -215,4 +215,309 @@ void TextLayoutCache::dumpCacheStats() { LOGD("------------------------------------------------"); } /** * TextLayoutCacheKey */ TextLayoutCacheKey::TextLayoutCacheKey() : text(NULL), start(0), count(0), contextCount(0), dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), hinting(SkPaint::kNo_Hinting) { } TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count, size_t contextCount, int dirFlags) : text(text), start(start), count(count), contextCount(contextCount), dirFlags(dirFlags) { typeface = paint->getTypeface(); textSize = paint->getTextSize(); textSkewX = paint->getTextSkewX(); textScaleX = paint->getTextScaleX(); flags = paint->getFlags(); hinting = paint->getHinting(); } bool TextLayoutCacheKey::operator<(const TextLayoutCacheKey& rhs) const { LTE_INT(count) { LTE_INT(contextCount) { LTE_INT(start) { LTE_INT(typeface) { LTE_FLOAT(textSize) { LTE_FLOAT(textSkewX) { LTE_FLOAT(textScaleX) { LTE_INT(flags) { LTE_INT(hinting) { LTE_INT(dirFlags) { return strncmp16(text, rhs.text, contextCount) < 0; } } } } } } } } } } return false; } void TextLayoutCacheKey::internalTextCopy() { textCopy.setTo(text, contextCount); text = textCopy.string(); } size_t TextLayoutCacheKey::getSize() { return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; } /** * TextLayoutCacheValue */ TextLayoutCacheValue::TextLayoutCacheValue() { advances = NULL; totalAdvance = 0; } TextLayoutCacheValue::~TextLayoutCacheValue() { delete[] advances; } void TextLayoutCacheValue::setElapsedTime(uint32_t time) { elapsedTime = time; } uint32_t TextLayoutCacheValue::getElapsedTime() { return elapsedTime; } void TextLayoutCacheValue::computeAdvances(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { advances = new float[count]; this->count = count; #if RTL_USE_HARFBUZZ computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #else computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #endif #if DEBUG_ADVANCES LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - " "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, totalAdvance, advances[0], advances[1], advances[2], advances[3]); #endif } void TextLayoutCacheValue::copyResult(jfloat* outAdvances, jfloat* outTotalAdvance) { memcpy(outAdvances, advances, count * sizeof(jfloat)); *outTotalAdvance = totalAdvance; } size_t TextLayoutCacheValue::getSize() { return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * count; } void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { bool isRTL = dirFlags & 0x1; font->klass = &harfbuzzSkiaClass; font->userData = 0; // The values which harfbuzzSkiaClass returns are already scaled to // pixel units, so we just set all these to one to disable further // scaling. font->x_ppem = 1; font->y_ppem = 1; font->x_scale = 1; font->y_scale = 1; memset(shaperItem, 0, sizeof(*shaperItem)); shaperItem->font = font; shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable); shaperItem->kerning_applied = false; // We cannot know, ahead of time, how many glyphs a given script run // will produce. We take a guess that script runs will not produce more // than twice as many glyphs as there are code points plus a bit of // padding and fallback if we find that we are wrong. createGlyphArrays(shaperItem, (contextCount + 2) * 2); // Free memory for clusters if needed and recreate the clusters array if (shaperItem->log_clusters) { delete shaperItem->log_clusters; } shaperItem->log_clusters = new unsigned short[contextCount]; shaperItem->item.pos = start; shaperItem->item.length = count; shaperItem->item.bidiLevel = isRTL; shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common; shaperItem->string = chars; shaperItem->stringLength = contextCount; fontData->typeFace = paint->getTypeface(); fontData->textSize = paint->getTextSize(); fontData->textSkewX = paint->getTextSkewX(); fontData->textScaleX = paint->getTextScaleX(); fontData->flags = paint->getFlags(); fontData->hinting = paint->getHinting(); shaperItem->font->userData = fontData; } void TextLayoutCacheValue::shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { // Setup Harfbuzz Shaper setupShaperItem(shaperItem, font, fontData, paint, chars, start, count, contextCount, dirFlags); // Shape resetGlyphArrays(shaperItem); while (!HB_ShapeItem(shaperItem)) { // We overflowed our arrays. Resize and retry. // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. deleteGlyphArrays(shaperItem); createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1); resetGlyphArrays(shaperItem); } } void TextLayoutCacheValue::computeAdvancesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { bool isRTL = dirFlags & 0x1; HB_ShaperItem shaperItem; HB_FontRec font; FontData fontData; shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count, contextCount, dirFlags); #if DEBUG_ADVANCES LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, shaperItem.kerning_applied); LOGD(" -- string= '%s'", String8(chars, contextCount).string()); LOGD(" -- isDevKernText=%d", paint->isDevKernText()); #endif jfloat totalAdvance = 0; for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); #if DEBUG_ADVANCES LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], totalAdvance); #endif } deleteGlyphArrays(&shaperItem); HB_FreeFace(shaperItem.face); *outTotalAdvance = totalAdvance; } void TextLayoutCacheValue::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); jchar* buffer = tempBuffer.get(); SkScalar* scalarArray = (SkScalar*)outAdvances; // this is where we'd call harfbuzz // for now we just use ushape.c size_t widths; const jchar* text; if (dirFlags & 0x1) { // rtl, call arabic shaping in case UErrorCode status = U_ZERO_ERROR; // Use fixed length since we need to keep start and count valid u_shapeArabic(chars, contextCount, buffer, contextCount, U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); // we shouldn't fail unless there's an out of memory condition, // in which case we're hosed anyway for (int i = start, e = i + count; i < e; ++i) { if (buffer[i] == UNICODE_NOT_A_CHAR) { buffer[i] = UNICODE_ZWSP; // zero-width-space for skia } } text = buffer + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } else { text = chars + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } jfloat totalAdvance = 0; if (widths < count) { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", widths); #endif // Skia operates on code points, not code units, so surrogate pairs return only // one value. Expand the result so we have one value per UTF-16 code unit. // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, // leaving the remaining widths zero. Not nice. for (size_t i = 0, p = 0; i < widths; ++i) { totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); if (p < count && text[p] >= UNICODE_FIRST_LOW_SURROGATE && text[p] < UNICODE_FIRST_PRIVATE_USE && text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { outAdvances[p++] = 0; } #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } else { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", count); #endif for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } *outTotalAdvance = totalAdvance; } void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem* shaperItem) { delete[] shaperItem->glyphs; delete[] shaperItem->attributes; delete[] shaperItem->advances; delete[] shaperItem->offsets; } void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem* shaperItem, int size) { shaperItem->glyphs = new HB_Glyph[size]; shaperItem->attributes = new HB_GlyphAttributes[size]; shaperItem->advances = new HB_Fixed[size]; shaperItem->offsets = new HB_FixedPoint[size]; shaperItem->num_glyphs = size; } void TextLayoutCacheValue::resetGlyphArrays(HB_ShaperItem* shaperItem) { int size = shaperItem->num_glyphs; // All the types here don't have pointers. It is safe to reset to // zero unless Harfbuzz breaks the compatibility in the future. memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0])); memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0])); memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0])); memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0])); } } // namespace android
core/jni/android/graphics/TextLayoutCache.h +25 −275 Original line number Diff line number Diff line Loading @@ -64,62 +64,24 @@ namespace android { */ class TextLayoutCacheKey { public: TextLayoutCacheKey() : text(NULL), start(0), count(0), contextCount(0), dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), hinting(SkPaint::kNo_Hinting) { } TextLayoutCacheKey(); TextLayoutCacheKey(const SkPaint* paint, const UChar* text, size_t start, size_t count, size_t contextCount, int dirFlags) : text(text), start(start), count(count), contextCount(contextCount), dirFlags(dirFlags) { typeface = paint->getTypeface(); textSize = paint->getTextSize(); textSkewX = paint->getTextSkewX(); textScaleX = paint->getTextScaleX(); flags = paint->getFlags(); hinting = paint->getHinting(); } size_t contextCount, int dirFlags); bool operator<(const TextLayoutCacheKey& rhs) const { LTE_INT(count) { LTE_INT(contextCount) { LTE_INT(start) { LTE_INT(typeface) { LTE_FLOAT(textSize) { LTE_FLOAT(textSkewX) { LTE_FLOAT(textScaleX) { LTE_INT(flags) { LTE_INT(hinting) { LTE_INT(dirFlags) { return strncmp16(text, rhs.text, contextCount) < 0; } } } } } } } } } } return false; } bool operator<(const TextLayoutCacheKey& rhs) const; // We need to copy the text when we insert the key into the cache itself. // We don't need to copy the text when we are only comparing keys. void internalTextCopy() { textCopy.setTo(text, contextCount); text = textCopy.string(); } /** * We need to copy the text when we insert the key into the cache itself. * We don't need to copy the text when we are only comparing keys. */ void internalTextCopy(); /** * Get the size of the Cache key. */ size_t getSize() { return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; } size_t getSize(); private: const UChar* text; Loading @@ -137,232 +99,41 @@ private: }; // TextLayoutCacheKey /* * TextLayoutCacheEntry is the Cache entry * TextLayoutCacheValue is the Cache value */ class TextLayoutCacheValue { public: TextLayoutCacheValue() { advances = NULL; totalAdvance = 0; } ~TextLayoutCacheValue() { delete[] advances; } void setElapsedTime(uint32_t time) { elapsedTime = time; } TextLayoutCacheValue(); ~TextLayoutCacheValue(); uint32_t getElapsedTime() { return elapsedTime; } void setElapsedTime(uint32_t time); uint32_t getElapsedTime(); void computeAdvances(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { advances = new float[count]; this->count = count; #if RTL_USE_HARFBUZZ computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #else computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, advances, &totalAdvance); #endif #if DEBUG_ADVANCES LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - " "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, totalAdvance, advances[0], advances[1], advances[2], advances[3]); #endif } size_t contextCount, int dirFlags); void copyResult(jfloat* outAdvances, jfloat* outTotalAdvance) { memcpy(outAdvances, advances, count * sizeof(jfloat)); *outTotalAdvance = totalAdvance; } void copyResult(jfloat* outAdvances, jfloat* outTotalAdvance); /** * Get the size of the Cache entry */ size_t getSize() { return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * count; } size_t getSize(); static void setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { bool isRTL = dirFlags & 0x1; font->klass = &harfbuzzSkiaClass; font->userData = 0; // The values which harfbuzzSkiaClass returns are already scaled to // pixel units, so we just set all these to one to disable further // scaling. font->x_ppem = 1; font->y_ppem = 1; font->x_scale = 1; font->y_scale = 1; memset(shaperItem, 0, sizeof(*shaperItem)); shaperItem->font = font; shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable); shaperItem->kerning_applied = false; // We cannot know, ahead of time, how many glyphs a given script run // will produce. We take a guess that script runs will not produce more // than twice as many glyphs as there are code points plus a bit of // padding and fallback if we find that we are wrong. createGlyphArrays(shaperItem, (contextCount + 2) * 2); // Free memory for clusters if needed and recreate the clusters array if (shaperItem->log_clusters) { delete shaperItem->log_clusters; } shaperItem->log_clusters = new unsigned short[contextCount]; shaperItem->item.pos = start; shaperItem->item.length = count; shaperItem->item.bidiLevel = isRTL; shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common; shaperItem->string = chars; shaperItem->stringLength = contextCount; fontData->typeFace = paint->getTypeface(); fontData->textSize = paint->getTextSize(); fontData->textSkewX = paint->getTextSkewX(); fontData->textScaleX = paint->getTextScaleX(); fontData->flags = paint->getFlags(); fontData->hinting = paint->getHinting(); shaperItem->font->userData = fontData; } int dirFlags); static void shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) { // Setup Harfbuzz Shaper setupShaperItem(shaperItem, font, fontData, paint, chars, start, count, contextCount, dirFlags); // Shape resetGlyphArrays(shaperItem); while (!HB_ShapeItem(shaperItem)) { // We overflowed our arrays. Resize and retry. // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. deleteGlyphArrays(shaperItem); createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1); resetGlyphArrays(shaperItem); } } int dirFlags); static void computeAdvancesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { bool isRTL = dirFlags & 0x1; HB_ShaperItem shaperItem; HB_FontRec font; FontData fontData; shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count, contextCount, dirFlags); #if DEBUG_ADVANCES LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, shaperItem.kerning_applied); LOGD(" -- string= '%s'", String8(chars, contextCount).string()); LOGD(" -- isDevKernText=%d", paint->isDevKernText()); #endif jfloat totalAdvance = 0; for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); #if DEBUG_ADVANCES LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], totalAdvance); #endif } deleteGlyphArrays(&shaperItem); HB_FreeFace(shaperItem.face); *outTotalAdvance = totalAdvance; } jfloat* outAdvances, jfloat* outTotalAdvance); static void computeAdvancesWithICU(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); jchar* buffer = tempBuffer.get(); SkScalar* scalarArray = (SkScalar*)outAdvances; // this is where we'd call harfbuzz // for now we just use ushape.c size_t widths; const jchar* text; if (dirFlags & 0x1) { // rtl, call arabic shaping in case UErrorCode status = U_ZERO_ERROR; // Use fixed length since we need to keep start and count valid u_shapeArabic(chars, contextCount, buffer, contextCount, U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); // we shouldn't fail unless there's an out of memory condition, // in which case we're hosed anyway for (int i = start, e = i + count; i < e; ++i) { if (buffer[i] == UNICODE_NOT_A_CHAR) { buffer[i] = UNICODE_ZWSP; // zero-width-space for skia } } text = buffer + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } else { text = chars + start; widths = paint->getTextWidths(text, count << 1, scalarArray); } jfloat totalAdvance = 0; if (widths < count) { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", widths); #endif // Skia operates on code points, not code units, so surrogate pairs return only // one value. Expand the result so we have one value per UTF-16 code unit. // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, // leaving the remaining widths zero. Not nice. for (size_t i = 0, p = 0; i < widths; ++i) { totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); if (p < count && text[p] >= UNICODE_FIRST_LOW_SURROGATE && text[p] < UNICODE_FIRST_PRIVATE_USE && text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { outAdvances[p++] = 0; } #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } else { #if DEBUG_ADVANCES LOGD("ICU -- count=%d", count); #endif for (size_t i = 0; i < count; i++) { totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); #if DEBUG_ADVANCES LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); #endif } } *outTotalAdvance = totalAdvance; } jfloat* outAdvances, jfloat* outTotalAdvance); private: jfloat* advances; Loading @@ -371,32 +142,11 @@ private: uint32_t elapsedTime; static void deleteGlyphArrays(HB_ShaperItem* shaperItem) { delete[] shaperItem->glyphs; delete[] shaperItem->attributes; delete[] shaperItem->advances; delete[] shaperItem->offsets; } static void createGlyphArrays(HB_ShaperItem* shaperItem, int size) { shaperItem->glyphs = new HB_Glyph[size]; shaperItem->attributes = new HB_GlyphAttributes[size]; shaperItem->advances = new HB_Fixed[size]; shaperItem->offsets = new HB_FixedPoint[size]; shaperItem->num_glyphs = size; } static void resetGlyphArrays(HB_ShaperItem* shaperItem) { int size = shaperItem->num_glyphs; // All the types here don't have pointers. It is safe to reset to // zero unless Harfbuzz breaks the compatibility in the future. memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0])); memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0])); memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0])); memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0])); } static void deleteGlyphArrays(HB_ShaperItem* shaperItem); static void createGlyphArrays(HB_ShaperItem* shaperItem, int size); static void resetGlyphArrays(HB_ShaperItem* shaperItem); }; // TextLayoutCacheEntry }; // TextLayoutCacheValue class TextLayoutCache: public OnEntryRemoved<TextLayoutCacheKey, TextLayoutCacheValue*> Loading