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