Loading core/jni/android/graphics/Canvas.cpp +5 −21 Original line number Diff line number Diff line Loading @@ -732,11 +732,7 @@ public: jcharArray text, int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint); #else TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas); #endif env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } Loading @@ -745,11 +741,7 @@ public: int start, int end, jfloat x, jfloat y, int flags, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint); #else TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas); #endif env->ReleaseStringChars(text, textArray); } Loading @@ -770,12 +762,14 @@ public: value = TextLayoutCache::getInstance().getValue(paint, textArray, start, count, contextCount, flags); if (value == NULL) { LOGE("Cannot get TextLayoutCache value"); LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(textArray + start, count).string()); return ; } #else value = new TextLayoutCacheValue(); value->computeValues(paint, textArray, start, count, contextCount, flags); value = new TextLayoutCacheValue(contextCount); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(textArray), start, count, contextCount, flags); #endif doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint); } Loading @@ -802,13 +796,8 @@ public: jfloat x, jfloat y, int dirFlags, SkPaint* paint) { jchar* chars = env->GetCharArrayElements(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex, count, contextCount, x, y, dirFlags, paint); #else TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex, count, contextCount, dirFlags, x, y, canvas); #endif env->ReleaseCharArrayElements(text, chars, JNI_ABORT); } Loading @@ -820,13 +809,8 @@ public: jint count = end - start; jint contextCount = contextEnd - contextStart; const jchar* chars = env->GetStringChars(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart, count, contextCount, x, y, dirFlags, paint); #else TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart, count, contextCount, dirFlags, x, y, canvas); #endif env->ReleaseStringChars(text, chars); } Loading core/jni/android/graphics/Paint.cpp +3 −65 Original line number Diff line number Diff line Loading @@ -350,14 +350,10 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); const jchar* textArray = env->GetCharArrayElements(text, NULL); jfloat result = 0; #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength, paint->getFlags(), NULL /* dont need all advances */, &result); #else // we double count, since measureText wants a byteLength SkScalar width = paint->measureText(textArray + index, count << 1); result = SkScalarToFloat(width); #endif env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; } Loading @@ -380,13 +376,9 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength, paint->getFlags(), NULL /* dont need all advances */, &width); #else width = SkScalarToFloat(paint->measureText(textArray + start, count << 1)); #endif env->ReleaseStringChars(text, textArray); return width; } Loading @@ -404,12 +396,9 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength, paint->getFlags(), NULL /* dont need all advances */, &width); #else width = SkScalarToFloat(paint->measureText(textArray, textLength << 1)); #endif env->ReleaseStringChars(text, textArray); return width; } Loading @@ -434,17 +423,9 @@ public: AutoJavaFloatArray autoWidths(env, widths, count); jfloat* widthsArray = autoWidths.ptr(); #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, text, 0, count, count, paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */); #else SkScalar* scalarArray = (SkScalar*)widthsArray; count = paint->getTextWidths(text, count << 1, scalarArray); for (int i = 0; i < count; i++) { widthsArray[i] = SkScalarToFloat(scalarArray[i]); } #endif return count; } Loading Loading @@ -597,54 +578,11 @@ public: static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start, jint count, jint flags, jint offset, jint opt) { #if RTL_USE_HARFBUZZ jfloat scalarArray[count]; TextLayout::getTextRunAdvances(paint, text, start, count, count, flags, scalarArray, NULL /* dont need totalAdvance */); #else SkScalar scalarArray[count]; jchar buffer[count]; // this is where we'd call harfbuzz // for now we just use ushape.c and widths returned from skia int widths; if (flags & 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(text + start, count, buffer, count, 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 = 0; i < count; ++i) { if (buffer[i] == 0xffff) { buffer[i] = 0x200b; // zero-width-space for skia } } widths = paint->getTextWidths(buffer, count << 1, scalarArray); } else { widths = paint->getTextWidths(text + start, count << 1, scalarArray); } if (widths < count) { // 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. const jchar *chars = text + start; for (int i = count, p = widths - 1; --i > p;) { if (chars[i] >= 0xdc00 && chars[i] < 0xe000 && chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) { scalarArray[i] = 0; } else { scalarArray[i] = scalarArray[--p]; } } } #endif jint pos = offset - start; switch (opt) { case AFTER: Loading core/jni/android/graphics/RtlProperties.h +0 −3 Original line number Diff line number Diff line Loading @@ -45,9 +45,6 @@ static RtlDebugLevel readRtlDebugLevel() { return kRtlDebugDisabled; } // Define if we want to use Harfbuzz (1) or not (0) #define RTL_USE_HARFBUZZ 1 // Define if we want (1) to have Advances debug values or not (0) #define DEBUG_ADVANCES 0 Loading core/jni/android/graphics/TextLayout.cpp +58 −218 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ #define LOG_TAG "TextLayout" #include "TextLayout.h" #include "TextLayoutCache.h" Loading Loading @@ -46,208 +48,32 @@ bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) { return false; } /** * Character-based Arabic shaping. * * We'll use harfbuzz and glyph-based shaping instead once we're set up for it. * * @context the text context * @start the start of the text to render * @count the length of the text to render, start + count must be <= contextCount * @contextCount the length of the context * @shaped where to put the shaped text, must have capacity for count uchars * @return the length of the shaped text, or -1 if error */ int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, jchar* shaped, UErrorCode& status) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); jchar* buffer = tempBuffer.get(); // Use fixed length since we need to keep start and count valid u_shapeArabic(context, 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); if (U_SUCCESS(status)) { // trim out UNICODE_NOT_A_CHAR following ligatures, if any int end = 0; for (int i = start, e = start + count; i < e; ++i) { if (buffer[i] != UNICODE_NOT_A_CHAR) { buffer[end++] = buffer[i]; } } count = end; // ALOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount); ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE | UBIDI_KEEP_BASE_COMBINING, &status); if (U_SUCCESS(status)) { return count; } } return -1; } /** * Basic character-based layout supporting rtl and arabic shaping. * Runs bidi on the text and generates a reordered, shaped line in buffer, returning * the length. * @text the text * @len the length of the text in uchars * @dir receives the resolved paragraph direction * @buffer the buffer to receive the reordered, shaped line. Must have capacity of * at least len jchars. * @flags line bidi flags * @return the length of the reordered, shaped line, or -1 if error */ jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int& dir, jchar* buffer, UErrorCode& status) { static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING | UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE; UBiDiLevel bidiReq = 0; switch (flags) { case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len; case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status); } int32_t result = -1; UBiDi* bidi = ubidi_open(); if (bidi) { ubidi_setPara(bidi, text, len, bidiReq, NULL, &status); if (U_SUCCESS(status)) { dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl int rc = ubidi_countRuns(bidi, &status); if (U_SUCCESS(status)) { // ALOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc); int32_t slen = 0; for (int i = 0; i < rc; ++i) { int32_t start; int32_t length; UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length); if (runDir == UBIDI_RTL) { slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status); } else { memcpy(buffer + slen, text + start, length * sizeof(jchar)); slen += length; } } if (U_SUCCESS(status)) { result = slen; } } } ubidi_close(bidi); } return result; } bool TextLayout::prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, const jchar** outText, int32_t* outBytes, jchar** outBuffer) { const jchar *workText = text; jchar *buffer = NULL; int dir = kDirection_LTR; if (needsLayout(text, len, bidiFlags)) { buffer =(jchar *) malloc(len * sizeof(jchar)); if (!buffer) { return false; } UErrorCode status = U_ZERO_ERROR; len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir if (!U_SUCCESS(status)) { ALOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status); free(buffer); return false; // can't render } workText = buffer; // use the shaped text } bool trimLeft = false; bool trimRight = false; SkPaint::Align horiz = paint->getTextAlign(); switch (horiz) { case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break; case SkPaint::kCenter_Align: trimLeft = trimRight = true; break; case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask); default: break; } const jchar* workLimit = workText + len; if (trimLeft) { while (workText < workLimit && *workText == ' ') { ++workText; } } if (trimRight) { while (workLimit > workText && *(workLimit - 1) == ' ') { --workLimit; } } *outBytes = (workLimit - workText) << 1; *outText = workText; *outBuffer = buffer; return true; } // Draws or gets the path of a paragraph of text on a single line, running bidi and shaping. // This will draw if canvas is not null, otherwise path must be non-null and it will create // a path representing the text that would have been drawn. void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) { const jchar *workText; jchar *buffer = NULL; int32_t workBytes; if (prepareText(paint, text, len, bidiFlags, &workText, &workBytes, &buffer)) { SkScalar x_ = SkFloatToScalar(x); SkScalar y_ = SkFloatToScalar(y); if (canvas) { canvas->drawText(workText, workBytes, x_, y_, *paint); } else { paint->getTextPath(workText, workBytes, x_, y_, path); } free(buffer); } } bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count, jsize contextCount, jchar* shaped) { UErrorCode status = U_ZERO_ERROR; count = shapeRtlText(context, start, count, contextCount, shaped, status); if (U_SUCCESS(status)) { return true; } else { LOGW("drawTextRun error %d\n", status); } return false; sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE // Return advances from the cache. Compute them if needed value = TextLayoutCache::getInstance().getValue(paint, text, 0, len, len, bidiFlags); #else value = new TextLayoutCacheValue(len); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(text), 0, len, len, bidiFlags); #endif if (value == NULL) { LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(text, len).string()); return ; } void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) { SkScalar x_ = SkFloatToScalar(x); SkScalar y_ = SkFloatToScalar(y); uint8_t rtl = dirFlags & 0x1; if (rtl) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(contextCount); if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) { canvas->drawText(buffer.get(), count << 1, x_, y_, *paint); } if (canvas) { canvas->drawText(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, *paint); } else { canvas->drawText(chars + start, count << 1, x_, y_, *paint); paint->getTextPath(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, path); } } Loading @@ -260,10 +86,15 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta value = TextLayoutCache::getInstance().getValue(paint, chars, start, count, contextCount, dirFlags); #else value = new TextLayoutCacheValue(); value->computeValues(paint, chars, start, count, contextCount, dirFlags); value = new TextLayoutCacheValue(contextCount); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(chars), start, count, contextCount, dirFlags); #endif if (value != NULL) { if (value == NULL) { LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(chars + start, count).string()); return ; } if (resultAdvances) { memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); } Loading @@ -271,7 +102,6 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta *resultTotalAdvance = value->getTotalAdvance(); } } } void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, Loading @@ -281,12 +111,6 @@ void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint resultAdvances, &resultTotalAdvance); } // Draws a paragraph of text on a single line, running bidi and shaping void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len, int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) { handleText(paint, text, len, bidiFlags, x, y, canvas, NULL); } void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkPath *path) { handleText(paint, text, len, bidiFlags, x, y, NULL, path); Loading @@ -305,14 +129,30 @@ void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, return; } SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(count); int dir = kDirection_LTR; UErrorCode status = U_ZERO_ERROR; count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status); if (U_SUCCESS(status)) { canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint); sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE value = TextLayoutCache::getInstance().getValue(paint, text, 0, count, count, bidiFlags); #else value = new TextLayoutCacheValue(count); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(text), 0, count, count, bidiFlags); #endif if (value == NULL) { LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(text, count).string()); return ; } // Save old text encoding SkPaint::TextEncoding oldEncoding = paint->getTextEncoding(); // Define Glyph encoding paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint); // Get back old encoding paint->setTextEncoding(oldEncoding); } void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, Loading core/jni/android/graphics/TextLayout.h +1 −21 Original line number Diff line number Diff line Loading @@ -62,13 +62,6 @@ enum { class TextLayout { public: /* * Draws a unidirectional run of text. */ static void drawTextRun(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, int dirFlags, jfloat x, jfloat y, SkCanvas* canvas); static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat* resultTotalAdvance); Loading @@ -77,9 +70,6 @@ public: jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat& resultTotalAdvance); static void drawText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas); static void getTextPath(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkPath* path); Loading @@ -87,19 +77,9 @@ public: int bidiFlags, jfloat hOffset, jfloat vOffset, SkPath* path, SkCanvas* canvas); static bool prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, const jchar** outText, int32_t* outBytes, jchar** outBuffer); static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count, jsize contextCount, jchar* shaped); private: static bool needsLayout(const jchar* text, jint len, jint bidiFlags); static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, jchar* shaped, UErrorCode& status); static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer, UErrorCode &status); static void handleText(SkPaint* paint, const jchar* text, jsize len, int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas, SkPath* path); Loading Loading
core/jni/android/graphics/Canvas.cpp +5 −21 Original line number Diff line number Diff line Loading @@ -732,11 +732,7 @@ public: jcharArray text, int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint); #else TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas); #endif env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } Loading @@ -745,11 +741,7 @@ public: int start, int end, jfloat x, jfloat y, int flags, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint); #else TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas); #endif env->ReleaseStringChars(text, textArray); } Loading @@ -770,12 +762,14 @@ public: value = TextLayoutCache::getInstance().getValue(paint, textArray, start, count, contextCount, flags); if (value == NULL) { LOGE("Cannot get TextLayoutCache value"); LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(textArray + start, count).string()); return ; } #else value = new TextLayoutCacheValue(); value->computeValues(paint, textArray, start, count, contextCount, flags); value = new TextLayoutCacheValue(contextCount); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(textArray), start, count, contextCount, flags); #endif doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint); } Loading @@ -802,13 +796,8 @@ public: jfloat x, jfloat y, int dirFlags, SkPaint* paint) { jchar* chars = env->GetCharArrayElements(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex, count, contextCount, x, y, dirFlags, paint); #else TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex, count, contextCount, dirFlags, x, y, canvas); #endif env->ReleaseCharArrayElements(text, chars, JNI_ABORT); } Loading @@ -820,13 +809,8 @@ public: jint count = end - start; jint contextCount = contextEnd - contextStart; const jchar* chars = env->GetStringChars(text, NULL); #if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart, count, contextCount, x, y, dirFlags, paint); #else TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart, count, contextCount, dirFlags, x, y, canvas); #endif env->ReleaseStringChars(text, chars); } Loading
core/jni/android/graphics/Paint.cpp +3 −65 Original line number Diff line number Diff line Loading @@ -350,14 +350,10 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); const jchar* textArray = env->GetCharArrayElements(text, NULL); jfloat result = 0; #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength, paint->getFlags(), NULL /* dont need all advances */, &result); #else // we double count, since measureText wants a byteLength SkScalar width = paint->measureText(textArray + index, count << 1); result = SkScalarToFloat(width); #endif env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; } Loading @@ -380,13 +376,9 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength, paint->getFlags(), NULL /* dont need all advances */, &width); #else width = SkScalarToFloat(paint->measureText(textArray + start, count << 1)); #endif env->ReleaseStringChars(text, textArray); return width; } Loading @@ -404,12 +396,9 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength, paint->getFlags(), NULL /* dont need all advances */, &width); #else width = SkScalarToFloat(paint->measureText(textArray, textLength << 1)); #endif env->ReleaseStringChars(text, textArray); return width; } Loading @@ -434,17 +423,9 @@ public: AutoJavaFloatArray autoWidths(env, widths, count); jfloat* widthsArray = autoWidths.ptr(); #if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, text, 0, count, count, paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */); #else SkScalar* scalarArray = (SkScalar*)widthsArray; count = paint->getTextWidths(text, count << 1, scalarArray); for (int i = 0; i < count; i++) { widthsArray[i] = SkScalarToFloat(scalarArray[i]); } #endif return count; } Loading Loading @@ -597,54 +578,11 @@ public: static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start, jint count, jint flags, jint offset, jint opt) { #if RTL_USE_HARFBUZZ jfloat scalarArray[count]; TextLayout::getTextRunAdvances(paint, text, start, count, count, flags, scalarArray, NULL /* dont need totalAdvance */); #else SkScalar scalarArray[count]; jchar buffer[count]; // this is where we'd call harfbuzz // for now we just use ushape.c and widths returned from skia int widths; if (flags & 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(text + start, count, buffer, count, 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 = 0; i < count; ++i) { if (buffer[i] == 0xffff) { buffer[i] = 0x200b; // zero-width-space for skia } } widths = paint->getTextWidths(buffer, count << 1, scalarArray); } else { widths = paint->getTextWidths(text + start, count << 1, scalarArray); } if (widths < count) { // 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. const jchar *chars = text + start; for (int i = count, p = widths - 1; --i > p;) { if (chars[i] >= 0xdc00 && chars[i] < 0xe000 && chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) { scalarArray[i] = 0; } else { scalarArray[i] = scalarArray[--p]; } } } #endif jint pos = offset - start; switch (opt) { case AFTER: Loading
core/jni/android/graphics/RtlProperties.h +0 −3 Original line number Diff line number Diff line Loading @@ -45,9 +45,6 @@ static RtlDebugLevel readRtlDebugLevel() { return kRtlDebugDisabled; } // Define if we want to use Harfbuzz (1) or not (0) #define RTL_USE_HARFBUZZ 1 // Define if we want (1) to have Advances debug values or not (0) #define DEBUG_ADVANCES 0 Loading
core/jni/android/graphics/TextLayout.cpp +58 −218 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ * limitations under the License. */ #define LOG_TAG "TextLayout" #include "TextLayout.h" #include "TextLayoutCache.h" Loading Loading @@ -46,208 +48,32 @@ bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) { return false; } /** * Character-based Arabic shaping. * * We'll use harfbuzz and glyph-based shaping instead once we're set up for it. * * @context the text context * @start the start of the text to render * @count the length of the text to render, start + count must be <= contextCount * @contextCount the length of the context * @shaped where to put the shaped text, must have capacity for count uchars * @return the length of the shaped text, or -1 if error */ int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, jchar* shaped, UErrorCode& status) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); jchar* buffer = tempBuffer.get(); // Use fixed length since we need to keep start and count valid u_shapeArabic(context, 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); if (U_SUCCESS(status)) { // trim out UNICODE_NOT_A_CHAR following ligatures, if any int end = 0; for (int i = start, e = start + count; i < e; ++i) { if (buffer[i] != UNICODE_NOT_A_CHAR) { buffer[end++] = buffer[i]; } } count = end; // ALOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount); ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE | UBIDI_KEEP_BASE_COMBINING, &status); if (U_SUCCESS(status)) { return count; } } return -1; } /** * Basic character-based layout supporting rtl and arabic shaping. * Runs bidi on the text and generates a reordered, shaped line in buffer, returning * the length. * @text the text * @len the length of the text in uchars * @dir receives the resolved paragraph direction * @buffer the buffer to receive the reordered, shaped line. Must have capacity of * at least len jchars. * @flags line bidi flags * @return the length of the reordered, shaped line, or -1 if error */ jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int& dir, jchar* buffer, UErrorCode& status) { static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING | UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE; UBiDiLevel bidiReq = 0; switch (flags) { case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len; case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status); } int32_t result = -1; UBiDi* bidi = ubidi_open(); if (bidi) { ubidi_setPara(bidi, text, len, bidiReq, NULL, &status); if (U_SUCCESS(status)) { dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl int rc = ubidi_countRuns(bidi, &status); if (U_SUCCESS(status)) { // ALOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc); int32_t slen = 0; for (int i = 0; i < rc; ++i) { int32_t start; int32_t length; UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length); if (runDir == UBIDI_RTL) { slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status); } else { memcpy(buffer + slen, text + start, length * sizeof(jchar)); slen += length; } } if (U_SUCCESS(status)) { result = slen; } } } ubidi_close(bidi); } return result; } bool TextLayout::prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, const jchar** outText, int32_t* outBytes, jchar** outBuffer) { const jchar *workText = text; jchar *buffer = NULL; int dir = kDirection_LTR; if (needsLayout(text, len, bidiFlags)) { buffer =(jchar *) malloc(len * sizeof(jchar)); if (!buffer) { return false; } UErrorCode status = U_ZERO_ERROR; len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir if (!U_SUCCESS(status)) { ALOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status); free(buffer); return false; // can't render } workText = buffer; // use the shaped text } bool trimLeft = false; bool trimRight = false; SkPaint::Align horiz = paint->getTextAlign(); switch (horiz) { case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break; case SkPaint::kCenter_Align: trimLeft = trimRight = true; break; case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask); default: break; } const jchar* workLimit = workText + len; if (trimLeft) { while (workText < workLimit && *workText == ' ') { ++workText; } } if (trimRight) { while (workLimit > workText && *(workLimit - 1) == ' ') { --workLimit; } } *outBytes = (workLimit - workText) << 1; *outText = workText; *outBuffer = buffer; return true; } // Draws or gets the path of a paragraph of text on a single line, running bidi and shaping. // This will draw if canvas is not null, otherwise path must be non-null and it will create // a path representing the text that would have been drawn. void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) { const jchar *workText; jchar *buffer = NULL; int32_t workBytes; if (prepareText(paint, text, len, bidiFlags, &workText, &workBytes, &buffer)) { SkScalar x_ = SkFloatToScalar(x); SkScalar y_ = SkFloatToScalar(y); if (canvas) { canvas->drawText(workText, workBytes, x_, y_, *paint); } else { paint->getTextPath(workText, workBytes, x_, y_, path); } free(buffer); } } bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count, jsize contextCount, jchar* shaped) { UErrorCode status = U_ZERO_ERROR; count = shapeRtlText(context, start, count, contextCount, shaped, status); if (U_SUCCESS(status)) { return true; } else { LOGW("drawTextRun error %d\n", status); } return false; sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE // Return advances from the cache. Compute them if needed value = TextLayoutCache::getInstance().getValue(paint, text, 0, len, len, bidiFlags); #else value = new TextLayoutCacheValue(len); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(text), 0, len, len, bidiFlags); #endif if (value == NULL) { LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(text, len).string()); return ; } void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) { SkScalar x_ = SkFloatToScalar(x); SkScalar y_ = SkFloatToScalar(y); uint8_t rtl = dirFlags & 0x1; if (rtl) { SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(contextCount); if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) { canvas->drawText(buffer.get(), count << 1, x_, y_, *paint); } if (canvas) { canvas->drawText(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, *paint); } else { canvas->drawText(chars + start, count << 1, x_, y_, *paint); paint->getTextPath(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, path); } } Loading @@ -260,10 +86,15 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta value = TextLayoutCache::getInstance().getValue(paint, chars, start, count, contextCount, dirFlags); #else value = new TextLayoutCacheValue(); value->computeValues(paint, chars, start, count, contextCount, dirFlags); value = new TextLayoutCacheValue(contextCount); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(chars), start, count, contextCount, dirFlags); #endif if (value != NULL) { if (value == NULL) { LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(chars + start, count).string()); return ; } if (resultAdvances) { memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); } Loading @@ -271,7 +102,6 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta *resultTotalAdvance = value->getTotalAdvance(); } } } void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, Loading @@ -281,12 +111,6 @@ void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint resultAdvances, &resultTotalAdvance); } // Draws a paragraph of text on a single line, running bidi and shaping void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len, int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) { handleText(paint, text, len, bidiFlags, x, y, canvas, NULL); } void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkPath *path) { handleText(paint, text, len, bidiFlags, x, y, NULL, path); Loading @@ -305,14 +129,30 @@ void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, return; } SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(count); int dir = kDirection_LTR; UErrorCode status = U_ZERO_ERROR; count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status); if (U_SUCCESS(status)) { canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint); sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE value = TextLayoutCache::getInstance().getValue(paint, text, 0, count, count, bidiFlags); #else value = new TextLayoutCacheValue(count); TextLayoutEngine::getInstance().computeValues(value.get(), paint, reinterpret_cast<const UChar*>(text), 0, count, count, bidiFlags); #endif if (value == NULL) { LOGE("Cannot get TextLayoutCache value for text = '%s'", String8(text, count).string()); return ; } // Save old text encoding SkPaint::TextEncoding oldEncoding = paint->getTextEncoding(); // Define Glyph encoding paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint); // Get back old encoding paint->setTextEncoding(oldEncoding); } void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, Loading
core/jni/android/graphics/TextLayout.h +1 −21 Original line number Diff line number Diff line Loading @@ -62,13 +62,6 @@ enum { class TextLayout { public: /* * Draws a unidirectional run of text. */ static void drawTextRun(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, int dirFlags, jfloat x, jfloat y, SkCanvas* canvas); static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat* resultTotalAdvance); Loading @@ -77,9 +70,6 @@ public: jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat& resultTotalAdvance); static void drawText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas); static void getTextPath(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkPath* path); Loading @@ -87,19 +77,9 @@ public: int bidiFlags, jfloat hOffset, jfloat vOffset, SkPath* path, SkCanvas* canvas); static bool prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, const jchar** outText, int32_t* outBytes, jchar** outBuffer); static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count, jsize contextCount, jchar* shaped); private: static bool needsLayout(const jchar* text, jint len, jint bidiFlags); static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, jchar* shaped, UErrorCode& status); static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer, UErrorCode &status); static void handleText(SkPaint* paint, const jchar* text, jsize len, int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas, SkPath* path); Loading