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

Commit 671d6cf4 authored by Romain Guy's avatar Romain Guy
Browse files

Full support for Canvas.drawPosText

This also introduces a small optimization when rendering text.

Change-Id: Iff620ac97bf878eaac406bccc6daa07052c93890
parent fb9ffe02
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -585,10 +585,10 @@ static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count

    const jchar* glyphs = value->getGlyphs();
    size_t glyphsCount = value->getGlyphsCount();
    if (count < int(glyphsCount)) glyphsCount = count;
    int bytesCount = glyphsCount * sizeof(jchar);

    renderer->drawPosText((const char*) glyphs, bytesCount,
            count < int(glyphsCount) ? count : glyphsCount, positions, paint);
    renderer->drawPosText((const char*) glyphs, bytesCount, glyphsCount, positions, paint);
}

static void android_view_GLES20Canvas_drawPosTextArray(JNIEnv* env, jobject clazz,
+0 −5
Original line number Diff line number Diff line
@@ -283,8 +283,6 @@ changed.</li>

        <li>{@link android.graphics.Canvas#drawPicture drawPicture()}</li>

        <li>{@link android.graphics.Canvas#drawPosText drawPosText()}</li>

        <li>{@link android.graphics.Canvas#drawTextOnPath drawTextOnPath()}</li>

        <li>{@link android.graphics.Canvas#drawVertices drawVertices()}</li>
@@ -318,9 +316,6 @@ changed.</li>
        <li>{@link android.graphics.Canvas#drawBitmapMesh drawBitmapMesh()}: colors array is
        ignored</li>

        <li>{@link android.graphics.Canvas#drawLines drawLines()}: anti-aliasing is not
        supported</li>

        <li>{@link android.graphics.Canvas#setDrawFilter setDrawFilter()}: can be set, but is
        ignored</li>
      </ul>
+112 −50
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
    }
}

void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
    int nPenX = x + glyph->mBitmapLeft;
    int nPenY = y + glyph->mBitmapTop;

@@ -115,7 +116,8 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds
    }
}

void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
    int nPenX = x + glyph->mBitmapLeft;
    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;

@@ -133,8 +135,8 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
            nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
}

void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
    int nPenX = x + glyph->mBitmapLeft;
    int nPenY = y + glyph->mBitmapTop;

@@ -181,13 +183,19 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
                bitmapW, bitmapH, NULL);
                bitmapW, bitmapH, NULL, NULL);
    } else {
        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
                0, 0, NULL);
                0, 0, NULL, NULL);
    }
}

void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
            int numGlyphs, int x, int y, const float* positions) {
    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
            0, 0, NULL, positions);
}

void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
        int numGlyphs, Rect *bounds) {
    if (bounds == NULL) {
@@ -195,31 +203,38 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le
        return;
    }
    bounds->set(1e6, -1e6, -1e6, 1e6);
    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
}

#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)

void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
        uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) {
    if (numGlyphs == 0 || text == NULL || len == 0) {
        return;
    }

    int glyphsCount = 0;

    text += start;

    static RenderGlyph gRenderGlyph[] = {
            &android::uirenderer::Font::drawCachedGlyph,
            &android::uirenderer::Font::drawCachedGlyphBitmap,
            &android::uirenderer::Font::measureCachedGlyph
    };
    RenderGlyph render = gRenderGlyph[mode];

    if (positions == NULL) {
        SkFixed prevRsbDelta = 0;

        float penX = x;
        int penY = y;
    int glyphsLeft = 1;
    if (numGlyphs > 0) {
        glyphsLeft = numGlyphs;
    }

    SkFixed prevRsbDelta = 0;
        penX += 0.5f;

    text += start;

    while (glyphsLeft > 0) {
        while (glyphsCount < numGlyphs) {
            glyph_t glyph = GET_GLYPH(text);

            // Reached the end of the string
@@ -233,24 +248,50 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len

            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
            if (cachedGlyph->mIsValid) {
            switch(mode) {
            case FRAMEBUFFER:
                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
                        bitmap, bitmapW, bitmapH, bounds, positions);
            }

            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);

            glyphsCount++;
        }
    } else {
        const SkPaint::Align align = paint->getTextAlign();

        // This is for renderPosText()
        while (glyphsCount < numGlyphs) {
            glyph_t glyph = GET_GLYPH(text);

            // Reached the end of the string
            if (IS_END_OF_STRING(glyph)) {
                break;
            case BITMAP:
                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
            }

            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);

            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
            if (cachedGlyph->mIsValid) {
                int penX = x + positions[(glyphsCount << 1)];
                int penY = y + positions[(glyphsCount << 1) + 1];

                switch (align) {
                    case SkPaint::kRight_Align:
                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
                        break;
            case MEASURE:
                measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
                    case SkPaint::kCenter_Align:
                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
                    default:
                        break;
                }
        }

        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
                (*this.*render)(cachedGlyph, penX, penY,
                        bitmap, bitmapW, bitmapH, bounds, positions);
            }

        // If we were given a specific number of glyphs, decrement
        if (numGlyphs > 0) {
            glyphsLeft--;
            glyphsCount++;
        }
    }
}
@@ -866,21 +907,15 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
    return image;
}

bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
    checkInit();

    if (!mCurrentFont) {
        ALOGE("No font set");
        return false;
    }

    mDrawn = false;
    mBounds = bounds;
    mClip = clip;
}

    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);

void FontRenderer::finishRender() {
    mBounds = NULL;
    mClip = NULL;

@@ -888,6 +923,33 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text
        issueDrawCommand();
        mCurrentQuadIndex = 0;
    }
}

bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
    if (!mCurrentFont) {
        ALOGE("No font set");
        return false;
    }

    initRender(clip, bounds);
    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
    finishRender();

    return mDrawn;
}

bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
        const float* positions, Rect* bounds) {
    if (!mCurrentFont) {
        ALOGE("No font set");
        return false;
    }

    initRender(clip, bounds);
    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
    finishRender();

    return mDrawn;
}
+24 −6
Original line number Diff line number Diff line
@@ -151,6 +151,10 @@ public:
    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
            int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
            uint32_t bitmapW = 0, uint32_t bitmapH = 0);

    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
            int numGlyphs, int x, int y, const float* positions);

    /**
     * Creates a new font associated with the specified font state.
     */
@@ -160,6 +164,8 @@ public:

protected:
    friend class FontRenderer;
    typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
            uint32_t, uint32_t, Rect*, const float*);

    enum RenderMode {
        FRAMEBUFFER,
@@ -169,7 +175,7 @@ protected:

    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
            int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
            uint32_t bitmapW, uint32_t bitmapH, Rect *bounds);
            uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);

    void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
            int numGlyphs, Rect *bounds);
@@ -184,10 +190,16 @@ protected:

    CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
    void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds);
    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);

    void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
            Rect* bounds, const float* pos);
    void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
            Rect* bounds, const float* pos);
    void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
            Rect* bounds, const float* pos);

    CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);

@@ -226,8 +238,12 @@ public:
    }

    void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
    // bounds is an out parameter
    bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
            uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
    // bounds is an out parameter
    bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
            uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds);

    struct DropShadow {
        DropShadow() { };
@@ -297,6 +313,8 @@ protected:
    void initVertexArrayBuffers();

    void checkInit();
    void initRender(const Rect* clip, Rect* bounds);
    void finishRender();

    String16 mLatinPrecache;
    void precacheLatin(SkPaint* paint);
+66 −12
Original line number Diff line number Diff line
@@ -2084,23 +2084,81 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,

void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
        const float* positions, SkPaint* paint) {
    if (text == NULL || count == 0) {
    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
        return;
    }
    if (mSnapshot->isIgnored()) return;

    // TODO: implement properly
    drawText(text, bytesCount, count, 0, 0, paint);
    // NOTE: Skia does not support perspective transform on drawPosText yet
    if (!mSnapshot->transform->isSimple()) {
        return;
    }

    float x = 0.0f;
    float y = 0.0f;
    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
    if (pureTranslate) {
        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
    }

    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
            paint->getTextSize());

    int alpha;
    SkXfermode::Mode mode;
    getAlphaAndMode(paint, &alpha, &mode);

    // Pick the appropriate texture filtering
    bool linearFilter = mSnapshot->transform->changesBounds();
    if (pureTranslate && !linearFilter) {
        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
    }

    mCaches.activeTexture(0);
    setupDraw();
    setupDrawDirtyRegionsDisabled();
    setupDrawWithTexture(true);
    setupDrawAlpha8Color(paint->getColor(), alpha);
    setupDrawColorFilter();
    setupDrawShader();
    setupDrawBlending(true, mode);
    setupDrawProgram();
    setupDrawModelView(x, y, x, y, pureTranslate, true);
    setupDrawTexture(fontRenderer.getTexture(linearFilter));
    setupDrawPureColorUniforms();
    setupDrawColorFilterUniforms();
    setupDrawShaderUniforms(pureTranslate);

    const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);

#if RENDER_LAYERS_AS_REGIONS
    bool hasActiveLayer = hasLayer();
#else
    bool hasActiveLayer = false;
#endif

    if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
            positions, hasActiveLayer ? &bounds : NULL)) {
#if RENDER_LAYERS_AS_REGIONS
        if (hasActiveLayer) {
            if (!pureTranslate) {
                mSnapshot->transform->mapRect(bounds);
            }
            dirtyLayerUnchecked(bounds, getRegion());
        }
#endif
    }
}

void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
        float x, float y, SkPaint* paint, float length) {
    if (text == NULL || count == 0) {
    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
        return;
    }
    if (mSnapshot->isIgnored()) return;

    // NOTE: AA and glyph id encoding are set in DisplayListRenderer.cpp

    switch (paint->getTextAlign()) {
        case SkPaint::kCenter_Align:
@@ -2177,10 +2235,6 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
        glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
    }

    if (paint->getAlpha() == 0 && paint->getXfermode() == NULL) {
        return;
    }

    // Pick the appropriate texture filtering
    bool linearFilter = mSnapshot->transform->changesBounds();
    if (pureTranslate && !linearFilter) {
Loading