Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -9343,6 +9343,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 graphics/java/android/graphics/Paint.java +9 −1 Original line number Diff line number Diff line Loading @@ -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. Loading libs/hwui/Caches.cpp +8 −3 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -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"); Loading libs/hwui/FontRenderer.cpp +216 −81 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ #include <cutils/properties.h> #include <utils/Functor.h> #include <utils/Log.h> #ifdef ANDROID_ENABLE_RENDERSCRIPT Loading @@ -35,6 +34,7 @@ #include "Debug.h" #include "Extensions.h" #include "FontRenderer.h" #include "OpenGLRenderer.h" #include "PixelBuffer.h" #include "Rect.h" Loading @@ -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 /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -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()) { Loading @@ -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); Loading @@ -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 Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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++) { Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading @@ -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(); Loading @@ -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(); Loading @@ -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(); Loading Loading @@ -407,6 +523,11 @@ void FontRenderer::issueDrawCommand() { texture->resetMesh(); } } } void FontRenderer::issueDrawCommand() { issueDrawCommand(mACacheTextures); issueDrawCommand(mRGBACacheTextures); mDrawn = true; } Loading Loading @@ -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(); Loading Loading @@ -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(); } Loading @@ -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 libs/hwui/FontRenderer.h +44 −6 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 { Loading @@ -55,6 +88,7 @@ public: FontRenderer(); ~FontRenderer(); void flushLargeCaches(Vector<CacheTexture*>& cacheTextures); void flushLargeCaches(); void setGammaTable(const uint8_t* gammaTable) { Loading @@ -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() { }; Loading @@ -100,7 +135,7 @@ public: mLinearFiltering = linearFiltering; } uint32_t getCacheSize() const; uint32_t getCacheSize(GLenum format) const; private: friend class Font; Loading @@ -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(); Loading @@ -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, Loading Loading @@ -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 Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -9343,6 +9343,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
graphics/java/android/graphics/Paint.java +9 −1 Original line number Diff line number Diff line Loading @@ -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. Loading
libs/hwui/Caches.cpp +8 −3 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -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"); Loading
libs/hwui/FontRenderer.cpp +216 −81 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ #include <cutils/properties.h> #include <utils/Functor.h> #include <utils/Log.h> #ifdef ANDROID_ENABLE_RENDERSCRIPT Loading @@ -35,6 +34,7 @@ #include "Debug.h" #include "Extensions.h" #include "FontRenderer.h" #include "OpenGLRenderer.h" #include "PixelBuffer.h" #include "Rect.h" Loading @@ -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 /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -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()) { Loading @@ -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); Loading @@ -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 Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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++) { Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading @@ -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(); Loading @@ -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(); Loading @@ -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(); Loading Loading @@ -407,6 +523,11 @@ void FontRenderer::issueDrawCommand() { texture->resetMesh(); } } } void FontRenderer::issueDrawCommand() { issueDrawCommand(mACacheTextures); issueDrawCommand(mRGBACacheTextures); mDrawn = true; } Loading Loading @@ -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(); Loading Loading @@ -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(); } Loading @@ -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
libs/hwui/FontRenderer.h +44 −6 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 { Loading @@ -55,6 +88,7 @@ public: FontRenderer(); ~FontRenderer(); void flushLargeCaches(Vector<CacheTexture*>& cacheTextures); void flushLargeCaches(); void setGammaTable(const uint8_t* gammaTable) { Loading @@ -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() { }; Loading @@ -100,7 +135,7 @@ public: mLinearFiltering = linearFiltering; } uint32_t getCacheSize() const; uint32_t getCacheSize(GLenum format) const; private: friend class Font; Loading @@ -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(); Loading @@ -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, Loading Loading @@ -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