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

Commit 1e546815 authored by Victoria Lease's avatar Victoria Lease
Browse files

Support RGBA fonts and bitmap fonts (and RGBA bitmap fonts)

Quite a few things going on in this commit:

- Enable bitmap strikes by default in Paint objects.

The SkPaint parameter that enables bitmap strikes was not previously
included in DEFAULT_PAINT_FLAGS. This effectively disabled bitmap
fonts. Oops! It's for the best, though, as additional work was needed
in Skia to make bitmap fonts work anyway.

- Complain if TEXTURE_BORDER_SIZE is not 1.

Our glyph cache code does not currently handle any value other than 1
here, including zero. I've added a little C preprocessor check to
prevent future engineers (including especially future-me) from
thinking that they can change this value without updating the related
code.

- Add GL_RGBA support to hwui's FontRenderer and friends

This also happened to involve some refactoring for convenience and
cleanliness.

Bug: 9577689
Change-Id: I0abd1e5a0d6623106247fb6421787e2c2f2ea19c
parent 3a6f2551
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -9342,6 +9342,7 @@ package android.graphics {
    field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
    field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
    field public static final int DITHER_FLAG = 4; // 0x4
    field public static final int EMBEDDED_BITMAP_TEXT_FLAG = 1024; // 0x400
    field public static final int FAKE_BOLD_TEXT_FLAG = 32; // 0x20
    field public static final int FILTER_BITMAP_FLAG = 2; // 0x2
    field public static final int HINTING_OFF = 0; // 0x0
+9 −1
Original line number Diff line number Diff line
@@ -105,9 +105,17 @@ public class Paint {
    public static final int SUBPIXEL_TEXT_FLAG  = 0x80;
    /** bit mask for the flag enabling device kerning for text */
    public static final int DEV_KERN_TEXT_FLAG  = 0x100;
    /** @hide bit mask for the flag enabling subpixel glyph rendering for text */
    public static final int LCD_RENDER_TEXT_FLAG = 0x200;
    /** bit mask for the flag enabling embedded bitmap strikes for text */
    public static final int EMBEDDED_BITMAP_TEXT_FLAG = 0x400;
    /** @hide bit mask for the flag forcing freetype's autohinter on for text */
    public static final int AUTO_HINTING_TEXT_FLAG = 0x800;
    /** @hide bit mask for the flag enabling vertical rendering for text */
    public static final int VERTICAL_TEXT_FLAG = 0x1000;

    // we use this when we first create a paint
    static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG;
    static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG;

    /**
     * Option for {@link #setHinting}: disable hinting.
+8 −3
Original line number Diff line number Diff line
@@ -256,8 +256,12 @@ void Caches::dumpMemoryUsage(String8 &log) {
    log.appendFormat("  PatchCache           %8d / %8d\n",
            patchCache.getSize(), patchCache.getMaxSize());
    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
        const uint32_t size = fontRenderer->getFontRendererSize(i);
        log.appendFormat("  FontRenderer %d       %8d / %8d\n", i, size, size);
        const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
        const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
        log.appendFormat("  FontRenderer %d A8    %8d / %8d\n", i, sizeA8, sizeA8);
        log.appendFormat("  FontRenderer %d RGBA  %8d / %8d\n", i, sizeRGBA, sizeRGBA);
        log.appendFormat("  FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
                sizeA8 + sizeRGBA);
    }
    log.appendFormat("Other:\n");
    log.appendFormat("  FboCache             %8d / %8d\n",
@@ -272,7 +276,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
    total += dropShadowCache.getSize();
    total += patchCache.getSize();
    for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
        total += fontRenderer->getFontRendererSize(i);
        total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
        total += fontRenderer->getFontRendererSize(i, GL_RGBA);
    }

    log.appendFormat("Total memory usage:\n");
+216 −81
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@

#include <cutils/properties.h>

#include <utils/Functor.h>
#include <utils/Log.h>

#ifdef ANDROID_ENABLE_RENDERSCRIPT
@@ -35,6 +34,7 @@
#include "Debug.h"
#include "Extensions.h"
#include "FontRenderer.h"
#include "OpenGLRenderer.h"
#include "PixelBuffer.h"
#include "Rect.h"

@@ -44,6 +44,52 @@ namespace uirenderer {
// blur inputs smaller than this constant will bypass renderscript
#define RS_MIN_INPUT_CUTOFF 10000

///////////////////////////////////////////////////////////////////////////////
// TextSetupFunctor
///////////////////////////////////////////////////////////////////////////////
status_t TextSetupFunctor::operator ()(int what, void* data) {
    Data* typedData = reinterpret_cast<Data*>(data);
    GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA;

    renderer->setupDraw();
    renderer->setupDrawTextGamma(paint);
    renderer->setupDrawDirtyRegionsDisabled();
    renderer->setupDrawWithTexture(glyphFormat == GL_ALPHA);
    switch (glyphFormat) {
        case GL_ALPHA: {
            renderer->setupDrawAlpha8Color(paint->getColor(), alpha);
            break;
        }
        case GL_RGBA: {
            float floatAlpha = alpha / 255.0f;
            renderer->setupDrawColor(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
            break;
        }
        default: {
#if DEBUG_FONT_RENDERER
            ALOGD("TextSetupFunctor: called with unknown glyph format %x", glyphFormat);
#endif
            break;
        }
    }
    renderer->setupDrawColorFilter();
    renderer->setupDrawShader();
    renderer->setupDrawBlending(true, mode);
    renderer->setupDrawProgram();
    renderer->setupDrawModelView(x, y, x, y, pureTranslate, true);
    // Calling setupDrawTexture with the name 0 will enable the
    // uv attributes and increase the texture unit count
    // texture binding will be performed by the font renderer as
    // needed
    renderer->setupDrawTexture(0);
    renderer->setupDrawPureColorUniforms();
    renderer->setupDrawColorFilterUniforms();
    renderer->setupDrawShaderUniforms(pureTranslate);
    renderer->setupDrawTextGammaUniforms();

    return NO_ERROR;
}

///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -103,11 +149,16 @@ FontRenderer::FontRenderer() :
    sLogFontRendererCreate = false;
}

FontRenderer::~FontRenderer() {
    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        delete mCacheTextures[i];
void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) {
    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
        delete cacheTextures[i];
    }
    cacheTextures.clear();
}
    mCacheTextures.clear();

FontRenderer::~FontRenderer() {
    clearCacheTextures(mACacheTextures);
    clearCacheTextures(mRGBACacheTextures);

    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    while (it.next()) {
@@ -124,15 +175,19 @@ void FontRenderer::flushAllAndInvalidate() {
        it.value()->invalidateTextureCache();
    }

    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        mCacheTextures[i]->init();
    for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
        mACacheTextures[i]->init();
    }

    for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
        mRGBACacheTextures[i]->init();
    }
}

void FontRenderer::flushLargeCaches() {
void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
    // Start from 1; don't deallocate smallest/default texture
    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
        CacheTexture* cacheTexture = mCacheTextures[i];
    for (uint32_t i = 1; i < cacheTextures.size(); i++) {
        CacheTexture* cacheTexture = cacheTextures[i];
        if (cacheTexture->getPixelBuffer()) {
            cacheTexture->init();
            LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
@@ -144,11 +199,16 @@ void FontRenderer::flushLargeCaches() {
    }
}

CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
        uint32_t* startX, uint32_t* startY) {
    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
            return mCacheTextures[i];
void FontRenderer::flushLargeCaches() {
    flushLargeCaches(mACacheTextures);
    flushLargeCaches(mRGBACacheTextures);
}

CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures,
        const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
        if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
            return cacheTextures[i];
        }
    }
    // Could not fit glyph into current cache textures
@@ -169,9 +229,26 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp

    cachedGlyph->mIsValid = false;

    // choose an appropriate cache texture list for this glyph format
    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
    Vector<CacheTexture*>* cacheTextures = NULL;
    switch (format) {
        case SkMask::kA8_Format:
            cacheTextures = &mACacheTextures;
            break;
        case SkMask::kARGB32_Format:
            cacheTextures = &mRGBACacheTextures;
            break;
        default:
#if DEBUG_FONT_RENDERER
            ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
#endif
        return;
    }

    // If the glyph is too tall, don't cache it
    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
                mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
                (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
                (int) glyph.fWidth, (int) glyph.fHeight);
        return;
@@ -181,14 +258,14 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
    uint32_t startX = 0;
    uint32_t startY = 0;

    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
    CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);

    if (!cacheTexture) {
        if (!precaching) {
            // If the new glyph didn't fit and we are not just trying to precache it,
            // clear out the cache and try again
            flushAllAndInvalidate();
            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
            cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
        }

        if (!cacheTexture) {
@@ -216,24 +293,21 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
        cacheTexture->allocateMesh();
    }

    // Tells us whether the glyphs is B&W (1 bit per pixel)
    // or anti-aliased (8 bits per pixel)
    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);

    uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;

    // Copy the glyph image, taking the mask format into account
    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
    int stride = glyph.rowBytes();

    uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    int srcStride = glyph.rowBytes();

    // Copy the glyph image, taking the mask format into account
    switch (format) {
        case SkMask::kA8_Format: {
            uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
                    - TEXTURE_BORDER_SIZE;
            // write leading border line
            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
            // write glyph data
            if (mGammaTable) {
                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
                    row = cacheY * cacheWidth;
                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
@@ -243,21 +317,55 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                }
            } else {
                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
                    row = cacheY * cacheWidth;
                    memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                }
            }
            // write trailing border line
            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
            break;
        }
        case SkMask::kARGB32_Format: {
            // prep data lengths
            const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
            const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
            size_t rowSize = formatSize * glyph.fWidth;
            // prep advances
            size_t dstStride = formatSize * cacheWidth;
            // prep indices
            // - we actually start one row early, and then increment before first copy
            uint8_t* src = &bitmapBuffer[0 - srcStride];
            uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
            uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
            uint8_t* dstL = dst - borderSize;
            uint8_t* dstR = dst + rowSize;
            // write leading border line
            memset(dstL, 0, rowSize + 2 * borderSize);
            // write glyph data
            while (dst < dstEnd) {
                memset(dstL += dstStride, 0, borderSize); // leading border column
                memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
                memset(dstR += dstStride, 0, borderSize); // trailing border column
            }
            // write trailing border line
            memset(dstL, 0, rowSize + 2 * borderSize);
            break;
        }
        case SkMask::kBW_Format: {
            uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
                    - TEXTURE_BORDER_SIZE;
            static const uint8_t COLORS[2] = { 0, 255 };

            // write leading border line
            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
            // write glyph data
            for (cacheY = startY; cacheY < endY; cacheY++) {
                cacheX = startX;
                int rowBytes = stride;
                int rowBytes = srcStride;
                uint8_t* buffer = bitmapBuffer;

                row = cacheY * cacheWidth;
@@ -270,23 +378,24 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
                }
                cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;

                bitmapBuffer += stride;
                bitmapBuffer += srcStride;
            }
            // write trailing border line
            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
            break;
        }
        default:
            ALOGW("Unkown glyph format: 0x%x", format);
            ALOGW("Unknown glyph format: 0x%x", format);
            break;
    }

    row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);

    cachedGlyph->mIsValid = true;
}

CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
    CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
        bool allocate) {
    CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads);

    if (allocate) {
        Caches::getInstance().activeTexture(0);
@@ -298,17 +407,23 @@ CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool alloc
}

void FontRenderer::initTextTexture() {
    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        delete mCacheTextures[i];
    }
    mCacheTextures.clear();
    clearCacheTextures(mACacheTextures);
    clearCacheTextures(mRGBACacheTextures);

    mUploadTexture = false;
    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
    mCurrentCacheTexture = mCacheTextures[0];
    mACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
            GL_ALPHA, true));
    mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
            GL_ALPHA, false));
    mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
            GL_ALPHA, false));
    mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
            GL_ALPHA, false));
    mRGBACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
            GL_RGBA, false));
    mRGBACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
            GL_RGBA, false));
    mCurrentCacheTexture = mACacheTextures[0];
}

// We don't want to allocate anything unless we actually draw text
@@ -322,20 +437,10 @@ void FontRenderer::checkInit() {
    mInitialized = true;
}

void FontRenderer::checkTextureUpdate() {
    if (!mUploadTexture) {
        return;
    }

    Caches& caches = Caches::getInstance();
    GLuint lastTextureId = 0;

    bool resetPixelStore = false;
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Iterate over all the cache textures and see which ones need to be updated
    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        CacheTexture* cacheTexture = mCacheTextures[i];
void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheTextures,
        bool& resetPixelStore, GLuint& lastTextureId) {
    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
        CacheTexture* cacheTexture = cacheTextures[i];
        if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
            if (cacheTexture->getTextureId() != lastTextureId) {
                lastTextureId = cacheTexture->getTextureId();
@@ -346,13 +451,24 @@ void FontRenderer::checkTextureUpdate() {
            if (cacheTexture->upload()) {
                resetPixelStore = true;
            }

#if DEBUG_FONT_RENDERER
            ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
                    i, x, y, width, height);
#endif
        }
    }
}

void FontRenderer::checkTextureUpdate() {
    if (!mUploadTexture) {
        return;
    }

    Caches& caches = Caches::getInstance();
    GLuint lastTextureId = 0;

    bool resetPixelStore = false;
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Iterate over all the cache textures and see which ones need to be updated
    checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
    checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);

    // Unbind any PBO we might have used to update textures
    caches.unbindPixelBuffer();
@@ -366,18 +482,18 @@ void FontRenderer::checkTextureUpdate() {
    mUploadTexture = false;
}

void FontRenderer::issueDrawCommand() {
void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
    Caches& caches = Caches::getInstance();
    bool first = true;
    bool force = false;

    GLuint lastId = 0;
    Caches& caches = Caches::getInstance();

    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        CacheTexture* texture = mCacheTextures[i];
    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
        CacheTexture* texture = cacheTextures[i];
        if (texture->canDraw()) {
            if (first) {
                if (mFunctor) (*mFunctor)(0, NULL);
                if (mFunctor) {
                    TextSetupFunctor::Data functorData(texture->getFormat());
                    (*mFunctor)(0, &functorData);
                }

                checkTextureUpdate();
                caches.bindIndicesBuffer();
@@ -407,6 +523,11 @@ void FontRenderer::issueDrawCommand() {
            texture->resetMesh();
        }
    }
}

void FontRenderer::issueDrawCommand() {
    issueDrawCommand(mACacheTextures);
    issueDrawCommand(mRGBACacheTextures);

    mDrawn = true;
}
@@ -582,13 +703,13 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t

bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
        float hOffset, float vOffset, Rect* bounds) {
        float hOffset, float vOffset, Rect* bounds, Functor* functor) {
    if (!mCurrentFont) {
        ALOGE("No font set");
        return false;
    }

    initRender(clip, bounds, NULL);
    initRender(clip, bounds, functor);
    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
    finishRender();

@@ -646,10 +767,10 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int
    delete[] scratch;
}

uint32_t FontRenderer::getCacheSize() const {
static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) {
    uint32_t size = 0;
    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
        CacheTexture* cacheTexture = mCacheTextures[i];
    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
        CacheTexture* cacheTexture = cacheTextures[i];
        if (cacheTexture && cacheTexture->getPixelBuffer()) {
            size += cacheTexture->getPixelBuffer()->getSize();
        }
@@ -657,5 +778,19 @@ uint32_t FontRenderer::getCacheSize() const {
    return size;
}

uint32_t FontRenderer::getCacheSize(GLenum format) const {
    switch (format) {
        case GL_ALPHA: {
            return calculateCacheSize(mACacheTextures);
        }
        case GL_RGBA: {
            return calculateCacheSize(mRGBACacheTextures);
        }
        default: {
            return 0;
        }
    }
}

}; // namespace uirenderer
}; // namespace android
+44 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H

#include <utils/Functor.h>
#include <utils/LruCache.h>
#include <utils/Vector.h>
#include <utils/StrongPointer.h>
@@ -46,8 +47,40 @@ class Functor;
namespace android {
namespace uirenderer {

class OpenGLRenderer;

///////////////////////////////////////////////////////////////////////////////
// TextSetupFunctor
///////////////////////////////////////////////////////////////////////////////
class TextSetupFunctor: public Functor {
public:
    struct Data {
        Data(GLenum glyphFormat) : glyphFormat(glyphFormat) {
        }

        GLenum glyphFormat;
    };

    TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
            int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
            renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
            alpha(alpha), mode(mode), paint(paint) {
    }
    ~TextSetupFunctor() { }

    status_t operator ()(int what, void* data);

    OpenGLRenderer* renderer;
    float x;
    float y;
    bool pureTranslate;
    int alpha;
    SkXfermode::Mode mode;
    SkPaint* paint;
};

///////////////////////////////////////////////////////////////////////////////
// Renderer
// FontRenderer
///////////////////////////////////////////////////////////////////////////////

class FontRenderer {
@@ -55,6 +88,7 @@ public:
    FontRenderer();
    ~FontRenderer();

    void flushLargeCaches(Vector<CacheTexture*>& cacheTextures);
    void flushLargeCaches();

    void setGammaTable(const uint8_t* gammaTable) {
@@ -73,7 +107,8 @@ public:

    // bounds is an out parameter
    bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
            uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
            uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds,
            Functor* functor);

    struct DropShadow {
        DropShadow() { };
@@ -100,7 +135,7 @@ public:
        mLinearFiltering = linearFiltering;
    }

    uint32_t getCacheSize() const;
    uint32_t getCacheSize(GLenum format) const;

private:
    friend class Font;
@@ -110,10 +145,11 @@ private:
    void allocateTextureMemory(CacheTexture* cacheTexture);
    void deallocateTextureMemory(CacheTexture* cacheTexture);
    void initTextTexture();
    CacheTexture* createCacheTexture(int width, int height, bool allocate);
    CacheTexture* createCacheTexture(int width, int height, GLenum format, bool allocate);
    void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
            uint32_t *retOriginX, uint32_t *retOriginY, bool precaching);
    CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
    CacheTexture* cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures, const SkGlyph& glyph,
            uint32_t* startX, uint32_t* startY);

    void flushAllAndInvalidate();

@@ -121,6 +157,7 @@ private:
    void initRender(const Rect* clip, Rect* bounds, Functor* functor);
    void finishRender();

    void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
    void issueDrawCommand();
    void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
            float x2, float y2, float u2, float v2,
@@ -148,7 +185,8 @@ private:
    uint32_t mLargeCacheWidth;
    uint32_t mLargeCacheHeight;

    Vector<CacheTexture*> mCacheTextures;
    Vector<CacheTexture*> mACacheTextures;
    Vector<CacheTexture*> mRGBACacheTextures;

    Font* mCurrentFont;
    LruCache<Font::FontDescription, Font*> mActiveFonts;
Loading