Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "FontRenderer.h"
     18 
     19 #include "Caches.h"
     20 #include "Debug.h"
     21 #include "Extensions.h"
     22 #include "Glop.h"
     23 #include "GlopBuilder.h"
     24 #include "PixelBuffer.h"
     25 #include "Rect.h"
     26 #include "renderstate/RenderState.h"
     27 #include "utils/Blur.h"
     28 #include "utils/Timing.h"
     29 
     30 
     31 #if HWUI_NEW_OPS
     32 #include "BakedOpDispatcher.h"
     33 #include "BakedOpRenderer.h"
     34 #include "BakedOpState.h"
     35 #else
     36 #include "OpenGLRenderer.h"
     37 #endif
     38 
     39 #include <algorithm>
     40 #include <cutils/properties.h>
     41 #include <SkGlyph.h>
     42 #include <SkUtils.h>
     43 #include <utils/Log.h>
     44 
     45 #ifdef ANDROID_ENABLE_RENDERSCRIPT
     46 #include <RenderScript.h>
     47 #endif
     48 
     49 namespace android {
     50 namespace uirenderer {
     51 
     52 // blur inputs smaller than this constant will bypass renderscript
     53 #define RS_MIN_INPUT_CUTOFF 10000
     54 
     55 ///////////////////////////////////////////////////////////////////////////////
     56 // TextSetupFunctor
     57 ///////////////////////////////////////////////////////////////////////////////
     58 
     59 void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
     60     int textureFillFlags = TextureFillFlags::None;
     61     if (texture.getFormat() == GL_ALPHA) {
     62         textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
     63     }
     64     if (linearFiltering) {
     65         textureFillFlags |= TextureFillFlags::ForceFilter;
     66     }
     67     int transformFlags = pureTranslate
     68             ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
     69     Glop glop;
     70 #if HWUI_NEW_OPS
     71     GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
     72             .setRoundRectClipState(bakedState->roundRectClipState)
     73             .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
     74             .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
     75             .setTransform(bakedState->computedState.transform, transformFlags)
     76             .setModelViewIdentityEmptyBounds()
     77             .build();
     78     // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
     79     renderer->renderGlop(nullptr, clip, glop);
     80 #else
     81     GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop)
     82             .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
     83             .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
     84             .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
     85             .setTransform(*(renderer->currentSnapshot()), transformFlags)
     86             .setModelViewOffsetRect(0, 0, Rect())
     87             .build();
     88     renderer->renderGlop(glop);
     89 #endif
     90 }
     91 
     92 ///////////////////////////////////////////////////////////////////////////////
     93 // FontRenderer
     94 ///////////////////////////////////////////////////////////////////////////////
     95 
     96 static bool sLogFontRendererCreate = true;
     97 
     98 FontRenderer::FontRenderer(const uint8_t* gammaTable)
     99         : mGammaTable(gammaTable)
    100         , mCurrentFont(nullptr)
    101         , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
    102         , mCurrentCacheTexture(nullptr)
    103         , mUploadTexture(false)
    104         , mFunctor(nullptr)
    105         , mClip(nullptr)
    106         , mBounds(nullptr)
    107         , mDrawn(false)
    108         , mInitialized(false)
    109         , mLinearFiltering(false) {
    110 
    111     if (sLogFontRendererCreate) {
    112         INIT_LOGD("Creating FontRenderer");
    113     }
    114 
    115     mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH,
    116             DEFAULT_TEXT_SMALL_CACHE_WIDTH);
    117     mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT,
    118             DEFAULT_TEXT_SMALL_CACHE_HEIGHT);
    119 
    120     mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH,
    121             DEFAULT_TEXT_LARGE_CACHE_WIDTH);
    122     mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT,
    123             DEFAULT_TEXT_LARGE_CACHE_HEIGHT);
    124 
    125     uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
    126 
    127     mSmallCacheWidth = std::min(mSmallCacheWidth, maxTextureSize);
    128     mSmallCacheHeight = std::min(mSmallCacheHeight, maxTextureSize);
    129     mLargeCacheWidth = std::min(mLargeCacheWidth, maxTextureSize);
    130     mLargeCacheHeight = std::min(mLargeCacheHeight, maxTextureSize);
    131 
    132     if (sLogFontRendererCreate) {
    133         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
    134                 mSmallCacheWidth, mSmallCacheHeight,
    135                 mLargeCacheWidth, mLargeCacheHeight >> 1,
    136                 mLargeCacheWidth, mLargeCacheHeight >> 1,
    137                 mLargeCacheWidth, mLargeCacheHeight);
    138     }
    139 
    140     sLogFontRendererCreate = false;
    141 }
    142 
    143 void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) {
    144     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    145         delete cacheTextures[i];
    146     }
    147     cacheTextures.clear();
    148 }
    149 
    150 FontRenderer::~FontRenderer() {
    151     clearCacheTextures(mACacheTextures);
    152     clearCacheTextures(mRGBACacheTextures);
    153 
    154     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    155     while (it.next()) {
    156         delete it.value();
    157     }
    158     mActiveFonts.clear();
    159 }
    160 
    161 void FontRenderer::flushAllAndInvalidate() {
    162     issueDrawCommand();
    163 
    164     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    165     while (it.next()) {
    166         it.value()->invalidateTextureCache();
    167     }
    168 
    169     for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
    170         mACacheTextures[i]->init();
    171 
    172 #ifdef BUGREPORT_FONT_CACHE_USAGE
    173         mHistoryTracker.glyphsCleared(mACacheTextures[i]);
    174 #endif
    175     }
    176 
    177     for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
    178         mRGBACacheTextures[i]->init();
    179 #ifdef BUGREPORT_FONT_CACHE_USAGE
    180         mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]);
    181 #endif
    182     }
    183 
    184     mDrawn = false;
    185 }
    186 
    187 void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) {
    188     // Start from 1; don't deallocate smallest/default texture
    189     for (uint32_t i = 1; i < cacheTextures.size(); i++) {
    190         CacheTexture* cacheTexture = cacheTextures[i];
    191         if (cacheTexture->getPixelBuffer()) {
    192             cacheTexture->init();
    193 #ifdef BUGREPORT_FONT_CACHE_USAGE
    194             mHistoryTracker.glyphsCleared(cacheTexture);
    195 #endif
    196             LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    197             while (it.next()) {
    198                 it.value()->invalidateTextureCache(cacheTexture);
    199             }
    200             cacheTexture->releasePixelBuffer();
    201         }
    202     }
    203 }
    204 
    205 void FontRenderer::flushLargeCaches() {
    206     flushLargeCaches(mACacheTextures);
    207     flushLargeCaches(mRGBACacheTextures);
    208 }
    209 
    210 CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures,
    211         const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
    212     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    213         if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
    214             return cacheTextures[i];
    215         }
    216     }
    217     // Could not fit glyph into current cache textures
    218     return nullptr;
    219 }
    220 
    221 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
    222         uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
    223     checkInit();
    224 
    225     // If the glyph bitmap is empty let's assum the glyph is valid
    226     // so we can avoid doing extra work later on
    227     if (glyph.fWidth == 0 || glyph.fHeight == 0) {
    228         cachedGlyph->mIsValid = true;
    229         cachedGlyph->mCacheTexture = nullptr;
    230         return;
    231     }
    232 
    233     cachedGlyph->mIsValid = false;
    234 
    235     // choose an appropriate cache texture list for this glyph format
    236     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
    237     std::vector<CacheTexture*>* cacheTextures = nullptr;
    238     switch (format) {
    239         case SkMask::kA8_Format:
    240         case SkMask::kBW_Format:
    241             cacheTextures = &mACacheTextures;
    242             break;
    243         case SkMask::kARGB32_Format:
    244             cacheTextures = &mRGBACacheTextures;
    245             break;
    246         default:
    247 #if DEBUG_FONT_RENDERER
    248             ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
    249 #endif
    250         return;
    251     }
    252 
    253     // If the glyph is too tall, don't cache it
    254     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
    255                 (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
    256         ALOGE("Font size too large to fit in cache. width, height = %i, %i",
    257                 (int) glyph.fWidth, (int) glyph.fHeight);
    258         return;
    259     }
    260 
    261     // Now copy the bitmap into the cache texture
    262     uint32_t startX = 0;
    263     uint32_t startY = 0;
    264 
    265     CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
    266 
    267     if (!cacheTexture) {
    268         if (!precaching) {
    269             // If the new glyph didn't fit and we are not just trying to precache it,
    270             // clear out the cache and try again
    271             flushAllAndInvalidate();
    272             cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
    273         }
    274 
    275         if (!cacheTexture) {
    276             // either the glyph didn't fit or we're precaching and will cache it when we draw
    277             return;
    278         }
    279     }
    280 
    281     cachedGlyph->mCacheTexture = cacheTexture;
    282 
    283     *retOriginX = startX;
    284     *retOriginY = startY;
    285 
    286     uint32_t endX = startX + glyph.fWidth;
    287     uint32_t endY = startY + glyph.fHeight;
    288 
    289     uint32_t cacheWidth = cacheTexture->getWidth();
    290 
    291     if (!cacheTexture->getPixelBuffer()) {
    292         Caches::getInstance().textureState().activateTexture(0);
    293         // Large-glyph texture memory is allocated only as needed
    294         cacheTexture->allocatePixelBuffer();
    295     }
    296     if (!cacheTexture->mesh()) {
    297         cacheTexture->allocateMesh();
    298     }
    299 
    300     uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
    301     uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
    302     int srcStride = glyph.rowBytes();
    303 
    304     // Copy the glyph image, taking the mask format into account
    305     switch (format) {
    306         case SkMask::kA8_Format: {
    307             uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
    308             uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
    309                     - TEXTURE_BORDER_SIZE;
    310             // write leading border line
    311             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    312             // write glyph data
    313             if (mGammaTable) {
    314                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
    315                     row = cacheY * cacheWidth;
    316                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    317                     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
    318                         uint8_t tempCol = bitmapBuffer[bY + bX];
    319                         cacheBuffer[row + cacheX] = mGammaTable[tempCol];
    320                     }
    321                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    322                 }
    323             } else {
    324                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
    325                     row = cacheY * cacheWidth;
    326                     memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
    327                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    328                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    329                 }
    330             }
    331             // write trailing border line
    332             row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    333             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    334             break;
    335         }
    336         case SkMask::kARGB32_Format: {
    337             // prep data lengths
    338             const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
    339             const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
    340             size_t rowSize = formatSize * glyph.fWidth;
    341             // prep advances
    342             size_t dstStride = formatSize * cacheWidth;
    343             // prep indices
    344             // - we actually start one row early, and then increment before first copy
    345             uint8_t* src = &bitmapBuffer[0 - srcStride];
    346             uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
    347             uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
    348             uint8_t* dstL = dst - borderSize;
    349             uint8_t* dstR = dst + rowSize;
    350             // write leading border line
    351             memset(dstL, 0, rowSize + 2 * borderSize);
    352             // write glyph data
    353             while (dst < dstEnd) {
    354                 memset(dstL += dstStride, 0, borderSize); // leading border column
    355                 memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
    356                 memset(dstR += dstStride, 0, borderSize); // trailing border column
    357             }
    358             // write trailing border line
    359             memset(dstL += dstStride, 0, rowSize + 2 * borderSize);
    360             break;
    361         }
    362         case SkMask::kBW_Format: {
    363             uint32_t cacheX = 0, cacheY = 0;
    364             uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
    365                     - TEXTURE_BORDER_SIZE;
    366             static const uint8_t COLORS[2] = { 0, 255 };
    367             // write leading border line
    368             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    369             // write glyph data
    370             for (cacheY = startY; cacheY < endY; cacheY++) {
    371                 cacheX = startX;
    372                 int rowBytes = srcStride;
    373                 uint8_t* buffer = bitmapBuffer;
    374 
    375                 row = cacheY * cacheWidth;
    376                 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    377                 while (--rowBytes >= 0) {
    378                     uint8_t b = *buffer++;
    379                     for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
    380                         cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
    381                     }
    382                 }
    383                 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    384 
    385                 bitmapBuffer += srcStride;
    386             }
    387             // write trailing border line
    388             row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    389             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    390             break;
    391         }
    392         default:
    393             ALOGW("Unknown glyph format: 0x%x", format);
    394             break;
    395     }
    396 
    397     cachedGlyph->mIsValid = true;
    398 
    399 #ifdef BUGREPORT_FONT_CACHE_USAGE
    400     mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight);
    401 #endif
    402 }
    403 
    404 CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
    405         bool allocate) {
    406     CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
    407 
    408     if (allocate) {
    409         Caches::getInstance().textureState().activateTexture(0);
    410         cacheTexture->allocatePixelBuffer();
    411         cacheTexture->allocateMesh();
    412     }
    413 
    414     return cacheTexture;
    415 }
    416 
    417 void FontRenderer::initTextTexture() {
    418     clearCacheTextures(mACacheTextures);
    419     clearCacheTextures(mRGBACacheTextures);
    420 
    421     mUploadTexture = false;
    422     mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
    423             GL_ALPHA, true));
    424     mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
    425             GL_ALPHA, false));
    426     mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
    427             GL_ALPHA, false));
    428     mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
    429             GL_ALPHA, false));
    430     mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
    431             GL_RGBA, false));
    432     mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
    433             GL_RGBA, false));
    434     mCurrentCacheTexture = mACacheTextures[0];
    435 }
    436 
    437 // We don't want to allocate anything unless we actually draw text
    438 void FontRenderer::checkInit() {
    439     if (mInitialized) {
    440         return;
    441     }
    442 
    443     initTextTexture();
    444 
    445     mInitialized = true;
    446 }
    447 
    448 void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures,
    449         bool& resetPixelStore, GLuint& lastTextureId) {
    450     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    451         CacheTexture* cacheTexture = cacheTextures[i];
    452         if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
    453             if (cacheTexture->getTextureId() != lastTextureId) {
    454                 lastTextureId = cacheTexture->getTextureId();
    455                 caches.textureState().activateTexture(0);
    456                 caches.textureState().bindTexture(lastTextureId);
    457             }
    458 
    459             if (cacheTexture->upload()) {
    460                 resetPixelStore = true;
    461             }
    462         }
    463     }
    464 }
    465 
    466 void FontRenderer::checkTextureUpdate() {
    467     if (!mUploadTexture) {
    468         return;
    469     }
    470 
    471     Caches& caches = Caches::getInstance();
    472     GLuint lastTextureId = 0;
    473 
    474     bool resetPixelStore = false;
    475 
    476     // Iterate over all the cache textures and see which ones need to be updated
    477     checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
    478     checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
    479 
    480     // Unbind any PBO we might have used to update textures
    481     caches.pixelBufferState().unbind();
    482 
    483     // Reset to default unpack row length to avoid affecting texture
    484     // uploads in other parts of the renderer
    485     if (resetPixelStore) {
    486         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    487     }
    488 
    489     mUploadTexture = false;
    490 }
    491 
    492 void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) {
    493     if (!mFunctor) return;
    494 
    495     bool first = true;
    496     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    497         CacheTexture* texture = cacheTextures[i];
    498         if (texture->canDraw()) {
    499             if (first) {
    500                 checkTextureUpdate();
    501                 first = false;
    502                 mDrawn = true;
    503             }
    504 
    505             mFunctor->draw(*texture, mLinearFiltering);
    506 
    507             texture->resetMesh();
    508         }
    509     }
    510 }
    511 
    512 void FontRenderer::issueDrawCommand() {
    513     issueDrawCommand(mACacheTextures);
    514     issueDrawCommand(mRGBACacheTextures);
    515 }
    516 
    517 void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
    518         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
    519         float x4, float y4, float u4, float v4, CacheTexture* texture) {
    520     if (texture != mCurrentCacheTexture) {
    521         // Now use the new texture id
    522         mCurrentCacheTexture = texture;
    523     }
    524 
    525     mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
    526             x3, y3, u3, v3, x4, y4, u4, v4);
    527 }
    528 
    529 void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
    530         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
    531         float x4, float y4, float u4, float v4, CacheTexture* texture) {
    532 
    533     if (mClip &&
    534             (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
    535         return;
    536     }
    537 
    538     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
    539 
    540     if (mBounds) {
    541         mBounds->left = std::min(mBounds->left, x1);
    542         mBounds->top = std::min(mBounds->top, y3);
    543         mBounds->right = std::max(mBounds->right, x3);
    544         mBounds->bottom = std::max(mBounds->bottom, y1);
    545     }
    546 
    547     if (mCurrentCacheTexture->endOfMesh()) {
    548         issueDrawCommand();
    549     }
    550 }
    551 
    552 void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
    553         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
    554         float x4, float y4, float u4, float v4, CacheTexture* texture) {
    555 
    556     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
    557 
    558     if (mBounds) {
    559         mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4))));
    560         mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4))));
    561         mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4))));
    562         mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4))));
    563     }
    564 
    565     if (mCurrentCacheTexture->endOfMesh()) {
    566         issueDrawCommand();
    567     }
    568 }
    569 
    570 void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
    571     mCurrentFont = Font::create(this, paint, matrix);
    572 }
    573 
    574 FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
    575         int numGlyphs, float radius, const float* positions) {
    576     checkInit();
    577 
    578     DropShadow image;
    579     image.width = 0;
    580     image.height = 0;
    581     image.image = nullptr;
    582     image.penX = 0;
    583     image.penY = 0;
    584 
    585     if (!mCurrentFont) {
    586         return image;
    587     }
    588 
    589     mDrawn = false;
    590     mClip = nullptr;
    591     mBounds = nullptr;
    592 
    593     Rect bounds;
    594     mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
    595 
    596     uint32_t intRadius = Blur::convertRadiusToInt(radius);
    597     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
    598     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
    599 
    600     uint32_t maxSize = Caches::getInstance().maxTextureSize;
    601     if (paddedWidth > maxSize || paddedHeight > maxSize) {
    602         return image;
    603     }
    604 
    605 #ifdef ANDROID_ENABLE_RENDERSCRIPT
    606     // Align buffers for renderscript usage
    607     if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
    608         paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
    609     }
    610     int size = paddedWidth * paddedHeight;
    611     uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
    612 #else
    613     int size = paddedWidth * paddedHeight;
    614     uint8_t* dataBuffer = (uint8_t*) malloc(size);
    615 #endif
    616 
    617     memset(dataBuffer, 0, size);
    618 
    619     int penX = intRadius - bounds.left;
    620     int penY = intRadius - bounds.bottom;
    621 
    622     if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
    623         // text has non-whitespace, so draw and blur to create the shadow
    624         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
    625         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
    626         mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
    627                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
    628 
    629         // Unbind any PBO we might have used
    630         Caches::getInstance().pixelBufferState().unbind();
    631 
    632         blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
    633     }
    634 
    635     image.width = paddedWidth;
    636     image.height = paddedHeight;
    637     image.image = dataBuffer;
    638     image.penX = penX;
    639     image.penY = penY;
    640 
    641     return image;
    642 }
    643 
    644 void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
    645     checkInit();
    646 
    647     mDrawn = false;
    648     mBounds = bounds;
    649     mFunctor = functor;
    650     mClip = clip;
    651 }
    652 
    653 void FontRenderer::finishRender() {
    654     mBounds = nullptr;
    655     mClip = nullptr;
    656 
    657     issueDrawCommand();
    658 }
    659 
    660 void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
    661         const SkMatrix& matrix) {
    662     Font* font = Font::create(this, paint, matrix);
    663     font->precache(paint, glyphs, numGlyphs);
    664 }
    665 
    666 void FontRenderer::endPrecaching() {
    667     checkTextureUpdate();
    668 }
    669 
    670 bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
    671         int numGlyphs, int x, int y, const float* positions,
    672         Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
    673     if (!mCurrentFont) {
    674         ALOGE("No font set");
    675         return false;
    676     }
    677 
    678     initRender(clip, bounds, functor);
    679     mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
    680 
    681     if (forceFinish) {
    682         finishRender();
    683     }
    684 
    685     return mDrawn;
    686 }
    687 
    688 bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
    689         int numGlyphs, const SkPath* path, float hOffset, float vOffset,
    690         Rect* bounds, TextDrawFunctor* functor) {
    691     if (!mCurrentFont) {
    692         ALOGE("No font set");
    693         return false;
    694     }
    695 
    696     initRender(clip, bounds, functor);
    697     mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
    698     finishRender();
    699 
    700     return mDrawn;
    701 }
    702 
    703 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
    704     uint32_t intRadius = Blur::convertRadiusToInt(radius);
    705 #ifdef ANDROID_ENABLE_RENDERSCRIPT
    706     if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
    707         uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
    708 
    709         if (mRs == nullptr) {
    710             mRs = new RSC::RS();
    711             // a null path is OK because there are no custom kernels used
    712             // hence nothing gets cached by RS
    713             if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
    714                 mRs.clear();
    715                 ALOGE("blur RS failed to init");
    716             } else {
    717                 mRsElement = RSC::Element::A_8(mRs);
    718                 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
    719             }
    720         }
    721         if (mRs != nullptr) {
    722             RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
    723             RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
    724                     RS_ALLOCATION_MIPMAP_NONE,
    725                     RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
    726                     *image);
    727             RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t,
    728                     RS_ALLOCATION_MIPMAP_NONE,
    729                     RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
    730                     outImage);
    731 
    732             mRsScript->setRadius(radius);
    733             mRsScript->setInput(ain);
    734             mRsScript->forEach(aout);
    735 
    736             // replace the original image's pointer, avoiding a copy back to the original buffer
    737             free(*image);
    738             *image = outImage;
    739 
    740             return;
    741         }
    742     }
    743 #endif
    744 
    745     std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
    746     Blur::generateGaussianWeights(gaussian.get(), radius);
    747 
    748     std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
    749     Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
    750     Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
    751 }
    752 
    753 static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
    754     uint32_t size = 0;
    755     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    756         CacheTexture* cacheTexture = cacheTextures[i];
    757         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    758             size += cacheTexture->getPixelBuffer()->getSize();
    759         }
    760     }
    761     return size;
    762 }
    763 
    764 static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
    765     uint32_t size = 0;
    766     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    767         CacheTexture* cacheTexture = cacheTextures[i];
    768         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    769             size += cacheTexture->calculateFreeMemory();
    770         }
    771     }
    772     return size;
    773 }
    774 
    775 const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const {
    776     switch (format) {
    777         case GL_ALPHA: {
    778             return mACacheTextures;
    779         }
    780         case GL_RGBA: {
    781             return mRGBACacheTextures;
    782         }
    783         default: {
    784             LOG_ALWAYS_FATAL("Unsupported format: %d", format);
    785             // Impossible to hit this, but the compiler doesn't know that
    786             return *(new std::vector<CacheTexture*>());
    787         }
    788     }
    789 }
    790 
    791 static void dumpTextures(String8& log, const char* tag,
    792         const std::vector<CacheTexture*>& cacheTextures) {
    793     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    794         CacheTexture* cacheTexture = cacheTextures[i];
    795         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    796             uint32_t free = cacheTexture->calculateFreeMemory();
    797             uint32_t total = cacheTexture->getPixelBuffer()->getSize();
    798             log.appendFormat("    %-4s texture %d     %8d / %8d\n", tag, i, total - free, total);
    799         }
    800     }
    801 }
    802 
    803 void FontRenderer::dumpMemoryUsage(String8& log) const {
    804     const uint32_t sizeA8 = getCacheSize(GL_ALPHA);
    805     const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA);
    806     const uint32_t sizeRGBA = getCacheSize(GL_RGBA);
    807     const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA);
    808     log.appendFormat("  FontRenderer A8      %8d / %8d\n", usedA8, sizeA8);
    809     dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA));
    810     log.appendFormat("  FontRenderer RGBA    %8d / %8d\n", usedRGBA, sizeRGBA);
    811     dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA));
    812     log.appendFormat("  FontRenderer total   %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA);
    813 }
    814 
    815 uint32_t FontRenderer::getCacheSize(GLenum format) const {
    816     return calculateCacheSize(cacheTexturesForFormat(format));
    817 }
    818 
    819 uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
    820     return calculateFreeCacheSize(cacheTexturesForFormat(format));
    821 }
    822 
    823 uint32_t FontRenderer::getSize() const {
    824     return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
    825 }
    826 
    827 }; // namespace uirenderer
    828 }; // namespace android
    829