Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63b06804 authored by Fabrice Di Meglio's avatar Fabrice Di Meglio Committed by Android (Google) Code Review
Browse files

Merge "Clean TextLayout code and remove RTL_USE_HARFBUZZ"

parents c2fc52dc b02d0ca5
Loading
Loading
Loading
Loading
+5 −21
Original line number Diff line number Diff line
@@ -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);
    }

@@ -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);
    }

@@ -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);
    }
@@ -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);
    }

@@ -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);
    }

+3 −65
Original line number Diff line number Diff line
@@ -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;
    }
@@ -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;
    }
@@ -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;
    }
@@ -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;
    }

@@ -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:
+0 −3
Original line number Diff line number Diff line
@@ -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

+58 −218
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
 * limitations under the License.
 */

#define LOG_TAG "TextLayout"

#include "TextLayout.h"
#include "TextLayoutCache.h"

@@ -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);
    }
}

@@ -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));
    }
@@ -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,
@@ -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);
@@ -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,
+1 −21
Original line number Diff line number Diff line
@@ -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);
@@ -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);

@@ -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