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

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

Merge "Fix bug #5438102 Double Arabic harakat overlap instead of stack In TextView"

parents 19774dda ef9bb3c3
Loading
Loading
Loading
Loading
+234 −63
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

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

extern "C" {
  #include "harfbuzz-unicode.h"
@@ -26,8 +27,21 @@ extern "C" {
namespace android {

//--------------------------------------------------------------------------------------------------
#define TYPEFACE_ARABIC "/system/fonts/DroidNaskh-Regular.ttf"
#define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
#define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"

#if USE_TEXT_LAYOUT_CACHE

    ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);

    static SkTypeface* gDefaultTypeface = SkFontHost::CreateTypeface(
            NULL, NULL, NULL, 0, SkTypeface::kNormal);

    static SkTypeface* gArabicTypeface = NULL;
    static SkTypeface* gHebrewRegularTypeface = NULL;
    static SkTypeface* gHebrewBoldTypeface = NULL;

#endif
//--------------------------------------------------------------------------------------------------

@@ -320,7 +334,7 @@ void TextLayoutCacheValue::computeValues(SkPaint* paint, const UChar* chars,
    computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
            &mAdvances, &mTotalAdvance, &mGlyphs);
#if DEBUG_ADVANCES
    LOGD("Advances - start=%d, count=%d, countextCount=%d, totalAdvance=%f", start, count,
    LOGD("Advances - start=%d, count=%d, contextCount=%d, totalAdvance=%f", start, count,
            contextCount, mTotalAdvance);
#endif
}
@@ -331,10 +345,7 @@ size_t TextLayoutCacheValue::getSize() {
}

void TextLayoutCacheValue::initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font,
        FontData* fontData, SkPaint* paint, const UChar* chars, size_t contextCount) {
    // Zero the Shaper struct
    memset(&shaperItem, 0, sizeof(shaperItem));

        FontData* fontData, SkPaint* paint, const UChar* chars, size_t count) {
    font->klass = &harfbuzzSkiaClass;
    font->userData = 0;

@@ -346,34 +357,31 @@ void TextLayoutCacheValue::initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec*
    font->x_scale = 1;
    font->y_scale = 1;

    shaperItem.font = font;
    shaperItem.face = HB_NewFace(shaperItem.font, harfbuzzSkiaGetTable);

    // Reset kerning
    shaperItem.kerning_applied = false;

    // Define font data
    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 = font;
    shaperItem.font->userData = fontData;

    // 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);
    createGlyphArrays(shaperItem, (count + 2) * 2);

    // Create log clusters array
    shaperItem.log_clusters = new unsigned short[contextCount];
    shaperItem.log_clusters = new unsigned short[count];

    // Set the string properties
    shaperItem.string = chars;
    shaperItem.stringLength = contextCount;
    shaperItem.stringLength = count;
}

void TextLayoutCacheValue::freeShaperItem(HB_ShaperItem& shaperItem) {
@@ -382,14 +390,87 @@ void TextLayoutCacheValue::freeShaperItem(HB_ShaperItem& shaperItem) {
    HB_FreeFace(shaperItem.face);
}

void TextLayoutCacheValue::shapeRun(HB_ShaperItem& shaperItem, size_t start, size_t count,
        bool isRTL) {
unsigned TextLayoutCacheValue::shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
        size_t count, bool isRTL) {
    // Update Harfbuzz Shaper
    shaperItem.item.pos = start;
    shaperItem.item.pos = 0;
    shaperItem.item.length = count;
    shaperItem.item.bidiLevel = isRTL;

    shaperItem.item.script = isRTL ? HB_Script_Arabic : HB_Script_Common;
    // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
    // This is needed as the Typeface used for shaping can be not the default one
    // when we are shapping any script that needs to use a fallback Font
    const uint16_t* text16 = (const uint16_t*)shaperItem.string;
    unsigned result = paint->getBaseGlyphCount(SkUTF16_NextUnichar(&text16));

    // Set the correct Typeface depending on the script
    FontData* data = reinterpret_cast<FontData*>(shaperItem.font->userData);
    switch(shaperItem.item.script) {
        case HB_Script_Arabic:
            if (!gArabicTypeface) {
                gArabicTypeface = SkTypeface::CreateFromFile(TYPEFACE_ARABIC);
            }
            data->typeFace = gArabicTypeface;
#if DEBUG_GLYPHS
            LOGD("Using Arabic Typeface");
#endif
            break;

        case HB_Script_Hebrew:
            if(paint->getTypeface()) {
                switch(paint->getTypeface()->style()) {
                    case SkTypeface::kNormal:
                    case SkTypeface::kItalic:
                    default:
                        if (!gHebrewRegularTypeface) {
                            gHebrewRegularTypeface = SkTypeface::CreateFromFile(
                                    TYPE_FACE_HEBREW_REGULAR);
                        }
                        data->typeFace = gHebrewRegularTypeface;
#if DEBUG_GLYPHS
                        LOGD("Using Hebrew Regular/Italic Typeface");
#endif
                        break;
                    case SkTypeface::kBold:
                    case SkTypeface::kBoldItalic:
                        if (!gHebrewBoldTypeface) {
                            gHebrewBoldTypeface = SkTypeface::CreateFromFile(
                                    TYPE_FACE_HEBREW_BOLD);
                        }
                        data->typeFace = gHebrewBoldTypeface;
#if DEBUG_GLYPHS
                        LOGD("Using Hebrew Bold/BoldItalic Typeface");
#endif
                        break;
                }
            } else {
                data->typeFace = gHebrewRegularTypeface;
#if DEBUG_GLYPHS
                        LOGD("Using Hebrew Regular Typeface");
#endif
            }
            break;

        default:
            if(paint->getTypeface()) {
                data->typeFace = paint->getTypeface();
#if DEBUG_GLYPHS
            LOGD("Using Paint Typeface");
#endif
            } else {
                data->typeFace = gDefaultTypeface;
#if DEBUG_GLYPHS
            LOGD("Using Default Typeface");
#endif
            }
    }

    shaperItem.face = HB_NewFace(data, harfbuzzSkiaGetTable);

#if DEBUG_GLYPHS
    LOGD("Run typeFace = %p", data->typeFace);
    LOGD("Run typeFace->uniqueID = %d", data->typeFace->uniqueID());
#endif

    // Shape
    while (!HB_ShapeItem(&shaperItem)) {
@@ -398,6 +479,38 @@ void TextLayoutCacheValue::shapeRun(HB_ShaperItem& shaperItem, size_t start, siz
        deleteGlyphArrays(shaperItem);
        createGlyphArrays(shaperItem, shaperItem.num_glyphs << 1);
    }

    return result;
}

HB_Script TextLayoutCacheValue::getScriptFromRun(const UChar* chars, size_t start, size_t count,
        bool isRTL) {
    ssize_t nextCodePoint = 0;
    uint32_t codePoint = utf16_to_code_point(chars + start, count, &nextCodePoint);

    HB_Script script = code_point_to_script(codePoint);

    if (script == HB_Script_Inherited) {
#if DEBUG_GLYPHS
         LOGD("Cannot find a correct script for code point=%d "
                 " Need to look at the next code points.", codePoint);
#endif
         while (script == HB_Script_Inherited && nextCodePoint < count) {
             codePoint = utf16_to_code_point(chars, count, &nextCodePoint);
             script = code_point_to_script(codePoint);
        }
    }
    if (script == HB_Script_Inherited) {
#if DEBUG_GLYPHS
        LOGD("Cannot find a correct script from the text."
                " Need to select a default script depending on the passed text direction.");
#endif
        script = isRTL ? HB_Script_Arabic : HB_Script_Common;
    }
#if DEBUG_GLYPHS
    LOGD("Found script=%d for chars='%s'", script, String8(chars + start, count).string());
#endif
    return script;
}

void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
@@ -488,8 +601,7 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar
                            LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d isRTL=%d",
                                    startRun, lengthRun, isRTL);
#endif
                            computeRunValuesWithHarfbuzz(shaperItem, paint,
                                    startRun, lengthRun, isRTL,
                            computeRunValuesWithHarfbuzz(paint, chars + startRun, lengthRun, isRTL,
                                    outAdvances, &runTotalAdvance, outGlyphs);

                            *outTotalAdvance += runTotalAdvance;
@@ -514,14 +626,10 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar
            LOGD("computeValuesWithHarfbuzz -- Using a SINGLE Run "
                    "-- run-start=%d run-len=%d isRTL=%d", start, count, isRTL);
#endif
            computeRunValuesWithHarfbuzz(shaperItem, paint,
                    start, count, isRTL,
            computeRunValuesWithHarfbuzz(paint, chars + start, count, isRTL,
                    outAdvances, outTotalAdvance, outGlyphs);
        }

        // Cleaning
        freeShaperItem(shaperItem);

#if DEBUG_GLYPHS
        LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", outGlyphs->size());
#endif
@@ -536,52 +644,107 @@ static void logGlyphs(HB_ShaperItem shaperItem) {
    }
}

void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(HB_ShaperItem& shaperItem, SkPaint* paint,
        size_t start, size_t count, bool isRTL,
void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
        size_t count, bool isRTL,
        Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
        Vector<jchar>* const outGlyphs) {

    shapeRun(shaperItem, start, count, isRTL);
    unsigned glyphBaseCount = 0;

    *outTotalAdvance = 0;
    jfloat totalAdvance = 0;

    unsigned numCodePoints = 0;

    ssize_t startFontRun = 0;
    ssize_t endFontRun = 0;
    ssize_t indexFontRun = isRTL ? count - 1 : 0;
    size_t countFontRun = 0;

    HB_ShaperItem shaperItem;
    HB_FontRec font;
    FontData fontData;

    // Zero the Shaper struct
    memset(&shaperItem, 0, sizeof(shaperItem));

    // Split the BiDi run into Script runs. Harfbuzz will populate the script into the shaperItem
    while((isRTL) ?
            hb_utf16_script_run_prev(&numCodePoints, &shaperItem.item, chars,
                    count, &indexFontRun):
            hb_utf16_script_run_next(&numCodePoints, &shaperItem.item, chars,
                    count, &indexFontRun)) {

        startFontRun = shaperItem.item.pos;
        countFontRun = shaperItem.item.length;
        endFontRun = startFontRun + countFontRun;

#if DEBUG_GLYPHS
        LOGD("Shaped Font Run with");
        LOGD("         -- isRTL=%d", isRTL);
        LOGD("         -- HB script=%d", shaperItem.item.script);
        LOGD("         -- startFontRun=%d", startFontRun);
        LOGD("         -- endFontRun=%d", endFontRun);
        LOGD("         -- countFontRun=%d", countFontRun);
        LOGD("         -- run='%s'", String8(chars + startFontRun, countFontRun).string());
        LOGD("         -- string='%s'", String8(chars, count).string());
#endif

        // Initialize Harfbuzz Shaper
        initShaperItem(shaperItem, &font, &fontData, paint, chars + startFontRun, countFontRun);

        // Shape the Font run and get the base glyph count for offsetting the glyphIDs later on
        glyphBaseCount = shapeFontRun(shaperItem, paint, countFontRun, isRTL);

#if DEBUG_GLYPHS
        LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
                shaperItem.kerning_applied);
    LOGD("         -- string= '%s'", String8(shaperItem.string + start, count).string());
        LOGD("         -- isDevKernText=%d", paint->isDevKernText());
        LOGD("         -- glyphBaseCount=%d", glyphBaseCount);

        logGlyphs(shaperItem);
#endif
        if (isRTL) {
            endFontRun = startFontRun;
#if DEBUG_GLYPHS
            LOGD("         -- updated endFontRun=%d", endFontRun);
#endif
        } else {
            startFontRun = endFontRun;
#if DEBUG_GLYPHS
            LOGD("         -- updated startFontRun=%d", startFontRun);
#endif
        }

        if (shaperItem.advances == NULL || shaperItem.num_glyphs == 0) {
#if DEBUG_GLYPHS
            LOGD("HARFBUZZ -- advances array is empty or num_glypth = 0");
#endif
        outAdvances->insertAt(0, outAdvances->size(), count);
        *outTotalAdvance = 0;
        return;
            outAdvances->insertAt(0, outAdvances->size(), countFontRun);
            continue;
        }

        // Get Advances and their total
        jfloat currentAdvance = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
    jfloat totalAdvance = currentAdvance;
        jfloat totalFontRunAdvance = currentAdvance;
        outAdvances->add(currentAdvance);
    for (size_t i = 1; i < count; i++) {
        for (size_t i = 1; i < countFontRun; i++) {
            size_t clusterPrevious = shaperItem.log_clusters[i - 1];
            size_t cluster = shaperItem.log_clusters[i];
            if (cluster == clusterPrevious) {
                outAdvances->add(0);
            } else {
                currentAdvance = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
            totalAdvance += currentAdvance;
                totalFontRunAdvance += currentAdvance;
                outAdvances->add(currentAdvance);
            }
        }
    *outTotalAdvance = totalAdvance;
        totalAdvance += totalFontRunAdvance;

#if DEBUG_ADVANCES
    for (size_t i = 0; i < count; i++) {
        for (size_t i = 0; i < countFontRun; i++) {
            LOGD("hb-adv[%d] = %f - log_clusters = %d - total = %f", i,
                (*outAdvances)[i], shaperItem.log_clusters[i], totalAdvance);
                    (*outAdvances)[i], shaperItem.log_clusters[i], totalFontRunAdvance);
        }
#endif

@@ -589,13 +752,18 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(HB_ShaperItem& shaperIte
        if (outGlyphs) {
            size_t countGlyphs = shaperItem.num_glyphs;
            for (size_t i = 0; i < countGlyphs; i++) {
            jchar glyph = (jchar) shaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
                jchar glyph = glyphBaseCount +
                        (jchar) shaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
#if DEBUG_GLYPHS
                LOGD("HARFBUZZ  -- glyph[%d]=%d", i, glyph);
#endif
                outGlyphs->add(glyph);
            }
        }
        // Cleaning
        freeShaperItem(shaperItem);
    }
    *outTotalAdvance = totalAdvance;
}

void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem& shaperItem) {
@@ -606,6 +774,9 @@ void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem& shaperItem) {
}

void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem& shaperItem, int size) {
#if DEBUG_GLYPHS
    LOGD("createGlyphArrays  -- size=%d", size);
#endif
    shaperItem.glyphs = new HB_Glyph[size];
    shaperItem.attributes = new HB_GlyphAttributes[size];
    shaperItem.advances = new HB_Fixed[size];
+8 −5
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@
#include <SkPaint.h>
#include <SkTemplates.h>
#include <SkUtils.h>
#include <SkScalerContext.h>
#include <SkAutoKern.h>

#include <unicode/ubidi.h>
@@ -162,17 +161,21 @@ private:
            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
            Vector<jchar>* const outGlyphs);

    static void computeRunValuesWithHarfbuzz(HB_ShaperItem& shaperItem, SkPaint* paint,
            size_t start, size_t count, bool isRTL,
    static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
            size_t count, bool isRTL,
            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
            Vector<jchar>* const outGlyphs);

    static void initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font, FontData* fontData,
            SkPaint* paint, const UChar* chars, size_t contextCount);
            SkPaint* paint, const UChar* chars, size_t count);

    static void freeShaperItem(HB_ShaperItem& shaperItem);

    static void shapeRun(HB_ShaperItem& shaperItem, size_t start, size_t count, bool isRTL);
    static unsigned shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
            size_t count, bool isRTL);

    static HB_Script getScriptFromRun(const UChar* chars, size_t start, size_t count,
            bool isRTL);

    static void deleteGlyphArrays(HB_ShaperItem& shaperItem);

+3 −0
Original line number Diff line number Diff line
@@ -2097,6 +2097,9 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
    }

    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
#if DEBUG_GLYPHS
    LOGD("OpenGLRenderer drawText() with FontID=%d", SkTypeface::UniqueID(paint->getTypeface()));
#endif
    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
            paint->getTextSize());