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