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 "BakedOpDispatcher.h"
     20 #include "BakedOpRenderer.h"
     21 #include "BakedOpState.h"
     22 #include "Caches.h"
     23 #include "Debug.h"
     24 #include "Extensions.h"
     25 #include "Glop.h"
     26 #include "GlopBuilder.h"
     27 #include "PixelBuffer.h"
     28 #include "Rect.h"
     29 #include "font/Font.h"
     30 #include "renderstate/RenderState.h"
     31 #include "utils/Blur.h"
     32 #include "utils/Timing.h"
     33 
     34 #include <RenderScript.h>
     35 #include <SkGlyph.h>
     36 #include <SkUtils.h>
     37 #include <utils/Log.h>
     38 #include <algorithm>
     39 
     40 namespace android {
     41 namespace uirenderer {
     42 
     43 // blur inputs smaller than this constant will bypass renderscript
     44 #define RS_MIN_INPUT_CUTOFF 10000
     45 
     46 ///////////////////////////////////////////////////////////////////////////////
     47 // TextSetupFunctor
     48 ///////////////////////////////////////////////////////////////////////////////
     49 
     50 void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
     51     int textureFillFlags = TextureFillFlags::None;
     52     if (texture.getFormat() == GL_ALPHA) {
     53         textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
     54     }
     55     if (linearFiltering) {
     56         textureFillFlags |= TextureFillFlags::ForceFilter;
     57     }
     58     int transformFlags =
     59             pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
     60 #ifdef ANDROID_ENABLE_LINEAR_BLENDING
     61     bool gammaCorrection = true;
     62 #else
     63     bool gammaCorrection = false;
     64 #endif
     65     Glop glop;
     66     GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
     67             .setRoundRectClipState(bakedState->roundRectClipState)
     68             .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
     69             .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
     70             .setGammaCorrection(gammaCorrection)
     71             .setTransform(bakedState->computedState.transform, transformFlags)
     72             .setModelViewIdentityEmptyBounds()
     73             .build();
     74     // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
     75     renderer->renderGlop(nullptr, clip, glop);
     76 }
     77 
     78 ///////////////////////////////////////////////////////////////////////////////
     79 // FontRenderer
     80 ///////////////////////////////////////////////////////////////////////////////
     81 
     82 static bool sLogFontRendererCreate = true;
     83 
     84 FontRenderer::FontRenderer(const uint8_t* gammaTable)
     85         : mGammaTable(gammaTable)
     86         , mCurrentFont(nullptr)
     87         , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
     88         , mCurrentCacheTexture(nullptr)
     89         , mUploadTexture(false)
     90         , mFunctor(nullptr)
     91         , mClip(nullptr)
     92         , mBounds(nullptr)
     93         , mDrawn(false)
     94         , mInitialized(false)
     95         , mLinearFiltering(false) {
     96     if (sLogFontRendererCreate) {
     97         INIT_LOGD("Creating FontRenderer");
     98     }
     99 
    100     auto deviceInfo = DeviceInfo::get();
    101     auto displayInfo = deviceInfo->displayInfo();
    102     int maxTextureSize = deviceInfo->maxTextureSize();
    103 
    104     // Adjust cache size based on Pixel's desnsity.
    105     constexpr float PIXEL_DENSITY = 2.6;
    106     const float densityRatio = displayInfo.density / PIXEL_DENSITY;
    107 
    108     // TODO: Most devices are hardcoded with this configuration, does it need to be dynamic?
    109     mSmallCacheWidth =
    110             OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
    111     mSmallCacheHeight =
    112             OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
    113     mLargeCacheWidth =
    114             OffscreenBuffer::computeIdealDimension(std::min(2048, maxTextureSize) * densityRatio);
    115     mLargeCacheHeight =
    116             OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
    117 
    118     if (sLogFontRendererCreate) {
    119         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
    120                   mSmallCacheWidth, mSmallCacheHeight, mLargeCacheWidth, mLargeCacheHeight >> 1,
    121                   mLargeCacheWidth, mLargeCacheHeight >> 1, mLargeCacheWidth, mLargeCacheHeight);
    122     }
    123 
    124     sLogFontRendererCreate = false;
    125 }
    126 
    127 void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) {
    128     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    129         delete cacheTextures[i];
    130     }
    131     cacheTextures.clear();
    132 }
    133 
    134 FontRenderer::~FontRenderer() {
    135     clearCacheTextures(mACacheTextures);
    136     clearCacheTextures(mRGBACacheTextures);
    137 
    138     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    139     while (it.next()) {
    140         delete it.value();
    141     }
    142     mActiveFonts.clear();
    143 }
    144 
    145 void FontRenderer::flushAllAndInvalidate() {
    146     issueDrawCommand();
    147 
    148     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    149     while (it.next()) {
    150         it.value()->invalidateTextureCache();
    151     }
    152 
    153     for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
    154         mACacheTextures[i]->init();
    155 
    156 #ifdef BUGREPORT_FONT_CACHE_USAGE
    157         mHistoryTracker.glyphsCleared(mACacheTextures[i]);
    158 #endif
    159     }
    160 
    161     for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
    162         mRGBACacheTextures[i]->init();
    163 #ifdef BUGREPORT_FONT_CACHE_USAGE
    164         mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]);
    165 #endif
    166     }
    167 
    168     mDrawn = false;
    169 }
    170 
    171 void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) {
    172     // Start from 1; don't deallocate smallest/default texture
    173     for (uint32_t i = 1; i < cacheTextures.size(); i++) {
    174         CacheTexture* cacheTexture = cacheTextures[i];
    175         if (cacheTexture->getPixelBuffer()) {
    176             cacheTexture->init();
    177 #ifdef BUGREPORT_FONT_CACHE_USAGE
    178             mHistoryTracker.glyphsCleared(cacheTexture);
    179 #endif
    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(std::vector<CacheTexture*>& cacheTextures,
    195                                                  const SkGlyph& glyph, uint32_t* startX,
    196                                                  uint32_t* startY) {
    197     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    198         if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
    199             return cacheTextures[i];
    200         }
    201     }
    202     // Could not fit glyph into current cache textures
    203     return nullptr;
    204 }
    205 
    206 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
    207                                uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
    208     checkInit();
    209 
    210     // If the glyph bitmap is empty let's assum the glyph is valid
    211     // so we can avoid doing extra work later on
    212     if (glyph.fWidth == 0 || glyph.fHeight == 0) {
    213         cachedGlyph->mIsValid = true;
    214         cachedGlyph->mCacheTexture = nullptr;
    215         return;
    216     }
    217 
    218     cachedGlyph->mIsValid = false;
    219 
    220     // choose an appropriate cache texture list for this glyph format
    221     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
    222     std::vector<CacheTexture*>* cacheTextures = nullptr;
    223     switch (format) {
    224         case SkMask::kA8_Format:
    225         case SkMask::kBW_Format:
    226             cacheTextures = &mACacheTextures;
    227             break;
    228         case SkMask::kARGB32_Format:
    229             cacheTextures = &mRGBACacheTextures;
    230             break;
    231         default:
    232 #if DEBUG_FONT_RENDERER
    233             ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
    234 #endif
    235             return;
    236     }
    237 
    238     // If the glyph is too tall, don't cache it
    239     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
    240         (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
    241         ALOGE("Font size too large to fit in cache. width, height = %i, %i", (int)glyph.fWidth,
    242               (int)glyph.fHeight);
    243         return;
    244     }
    245 
    246     // Now copy the bitmap into the cache texture
    247     uint32_t startX = 0;
    248     uint32_t startY = 0;
    249 
    250     CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
    251 
    252     if (!cacheTexture) {
    253         if (!precaching) {
    254             // If the new glyph didn't fit and we are not just trying to precache it,
    255             // clear out the cache and try again
    256             flushAllAndInvalidate();
    257             cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
    258         }
    259 
    260         if (!cacheTexture) {
    261             // either the glyph didn't fit or we're precaching and will cache it when we draw
    262             return;
    263         }
    264     }
    265 
    266     cachedGlyph->mCacheTexture = cacheTexture;
    267 
    268     *retOriginX = startX;
    269     *retOriginY = startY;
    270 
    271     uint32_t endX = startX + glyph.fWidth;
    272     uint32_t endY = startY + glyph.fHeight;
    273 
    274     uint32_t cacheWidth = cacheTexture->getWidth();
    275 
    276     if (!cacheTexture->getPixelBuffer()) {
    277         Caches::getInstance().textureState().activateTexture(0);
    278         // Large-glyph texture memory is allocated only as needed
    279         cacheTexture->allocatePixelBuffer();
    280     }
    281     if (!cacheTexture->mesh()) {
    282         cacheTexture->allocateMesh();
    283     }
    284 
    285     uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
    286     uint8_t* bitmapBuffer = (uint8_t*)glyph.fImage;
    287     int srcStride = glyph.rowBytes();
    288 
    289     // Copy the glyph image, taking the mask format into account
    290     switch (format) {
    291         case SkMask::kA8_Format: {
    292             uint32_t row =
    293                     (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - 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 (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
    299                     row = cacheY * cacheWidth;
    300                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    301                     for (uint32_t 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 (uint32_t 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 =
    349                     (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - 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 #ifdef BUGREPORT_FONT_CACHE_USAGE
    384     mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight);
    385 #endif
    386 }
    387 
    388 CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
    389                                                bool allocate) {
    390     CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
    391 
    392     if (allocate) {
    393         Caches::getInstance().textureState().activateTexture(0);
    394         cacheTexture->allocatePixelBuffer();
    395         cacheTexture->allocateMesh();
    396     }
    397 
    398     return cacheTexture;
    399 }
    400 
    401 void FontRenderer::initTextTexture() {
    402     clearCacheTextures(mACacheTextures);
    403     clearCacheTextures(mRGBACacheTextures);
    404 
    405     mUploadTexture = false;
    406     mACacheTextures.push_back(
    407             createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_ALPHA, true));
    408     mACacheTextures.push_back(
    409             createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false));
    410     mACacheTextures.push_back(
    411             createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false));
    412     mACacheTextures.push_back(
    413             createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, GL_ALPHA, false));
    414     mRGBACacheTextures.push_back(
    415             createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_RGBA, false));
    416     mRGBACacheTextures.push_back(
    417             createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_RGBA, false));
    418     mCurrentCacheTexture = mACacheTextures[0];
    419 }
    420 
    421 // We don't want to allocate anything unless we actually draw text
    422 void FontRenderer::checkInit() {
    423     if (mInitialized) {
    424         return;
    425     }
    426 
    427     initTextTexture();
    428 
    429     mInitialized = true;
    430 }
    431 
    432 void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures,
    433                                 bool& resetPixelStore, GLuint& lastTextureId) {
    434     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    435         CacheTexture* cacheTexture = cacheTextures[i];
    436         if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
    437             if (cacheTexture->getTextureId() != lastTextureId) {
    438                 lastTextureId = cacheTexture->getTextureId();
    439                 caches.textureState().activateTexture(0);
    440                 caches.textureState().bindTexture(lastTextureId);
    441             }
    442 
    443             if (cacheTexture->upload()) {
    444                 resetPixelStore = true;
    445             }
    446         }
    447     }
    448 }
    449 
    450 void FontRenderer::checkTextureUpdate() {
    451     if (!mUploadTexture) {
    452         return;
    453     }
    454 
    455     Caches& caches = Caches::getInstance();
    456     GLuint lastTextureId = 0;
    457 
    458     bool resetPixelStore = false;
    459 
    460     // Iterate over all the cache textures and see which ones need to be updated
    461     checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
    462     checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
    463 
    464     // Unbind any PBO we might have used to update textures
    465     caches.pixelBufferState().unbind();
    466 
    467     // Reset to default unpack row length to avoid affecting texture
    468     // uploads in other parts of the renderer
    469     if (resetPixelStore) {
    470         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    471     }
    472 
    473     mUploadTexture = false;
    474 }
    475 
    476 void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) {
    477     if (!mFunctor) return;
    478 
    479     bool first = true;
    480     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    481         CacheTexture* texture = cacheTextures[i];
    482         if (texture->canDraw()) {
    483             if (first) {
    484                 checkTextureUpdate();
    485                 first = false;
    486                 mDrawn = true;
    487             }
    488 
    489             mFunctor->draw(*texture, mLinearFiltering);
    490 
    491             texture->resetMesh();
    492         }
    493     }
    494 }
    495 
    496 void FontRenderer::issueDrawCommand() {
    497     issueDrawCommand(mACacheTextures);
    498     issueDrawCommand(mRGBACacheTextures);
    499 }
    500 
    501 void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2,
    502                                         float u2, float v2, float x3, float y3, float u3, float v3,
    503                                         float x4, float y4, float u4, float v4,
    504                                         CacheTexture* texture) {
    505     if (texture != mCurrentCacheTexture) {
    506         // Now use the new texture id
    507         mCurrentCacheTexture = texture;
    508     }
    509 
    510     mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4);
    511 }
    512 
    513 void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2,
    514                                   float u2, float v2, float x3, float y3, float u3, float v3,
    515                                   float x4, float y4, float u4, float v4, CacheTexture* texture) {
    516     if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
    517         return;
    518     }
    519 
    520     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
    521 
    522     if (mBounds) {
    523         mBounds->left = std::min(mBounds->left, x1);
    524         mBounds->top = std::min(mBounds->top, y3);
    525         mBounds->right = std::max(mBounds->right, x3);
    526         mBounds->bottom = std::max(mBounds->bottom, y1);
    527     }
    528 
    529     if (mCurrentCacheTexture->endOfMesh()) {
    530         issueDrawCommand();
    531     }
    532 }
    533 
    534 void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2,
    535                                          float u2, float v2, float x3, float y3, float u3, float v3,
    536                                          float x4, float y4, float u4, float v4,
    537                                          CacheTexture* texture) {
    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, std::min(x1, std::min(x2, std::min(x3, x4))));
    542         mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4))));
    543         mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4))));
    544         mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4))));
    545     }
    546 
    547     if (mCurrentCacheTexture->endOfMesh()) {
    548         issueDrawCommand();
    549     }
    550 }
    551 
    552 void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
    553     mCurrentFont = Font::create(this, paint, matrix);
    554 }
    555 
    556 FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t* glyphs,
    557                                                         int numGlyphs, float radius,
    558                                                         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, glyphs, 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     // Align buffers for renderscript usage
    589     if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
    590         paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
    591     }
    592     int size = paddedWidth * paddedHeight;
    593     uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
    594 
    595     memset(dataBuffer, 0, size);
    596 
    597     int penX = intRadius - bounds.left;
    598     int penY = intRadius - bounds.bottom;
    599 
    600     if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
    601         // text has non-whitespace, so draw and blur to create the shadow
    602         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
    603         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
    604         mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, Font::BITMAP, dataBuffer,
    605                              paddedWidth, paddedHeight, nullptr, positions);
    606 
    607         // Unbind any PBO we might have used
    608         Caches::getInstance().pixelBufferState().unbind();
    609 
    610         blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
    611     }
    612 
    613     image.width = paddedWidth;
    614     image.height = paddedHeight;
    615     image.image = dataBuffer;
    616     image.penX = penX;
    617     image.penY = penY;
    618 
    619     return image;
    620 }
    621 
    622 void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
    623     checkInit();
    624 
    625     mDrawn = false;
    626     mBounds = bounds;
    627     mFunctor = functor;
    628     mClip = clip;
    629 }
    630 
    631 void FontRenderer::finishRender() {
    632     mBounds = nullptr;
    633     mClip = nullptr;
    634 
    635     issueDrawCommand();
    636 }
    637 
    638 void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
    639                             const SkMatrix& matrix) {
    640     Font* font = Font::create(this, paint, matrix);
    641     font->precache(paint, glyphs, numGlyphs);
    642 }
    643 
    644 void FontRenderer::endPrecaching() {
    645     checkTextureUpdate();
    646 }
    647 
    648 bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
    649                                  int numGlyphs, int x, int y, const float* positions, Rect* bounds,
    650                                  TextDrawFunctor* functor, bool forceFinish) {
    651     if (!mCurrentFont) {
    652         ALOGE("No font set");
    653         return false;
    654     }
    655 
    656     initRender(clip, bounds, functor);
    657     mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
    658 
    659     if (forceFinish) {
    660         finishRender();
    661     }
    662 
    663     return mDrawn;
    664 }
    665 
    666 bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
    667                                     int numGlyphs, const SkPath* path, float hOffset, float vOffset,
    668                                     Rect* bounds, TextDrawFunctor* functor) {
    669     if (!mCurrentFont) {
    670         ALOGE("No font set");
    671         return false;
    672     }
    673 
    674     initRender(clip, bounds, functor);
    675     mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
    676     finishRender();
    677 
    678     return mDrawn;
    679 }
    680 
    681 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
    682     uint32_t intRadius = Blur::convertRadiusToInt(radius);
    683     if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
    684         uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
    685 
    686         if (mRs == nullptr) {
    687             mRs = new RSC::RS();
    688             // a null path is OK because there are no custom kernels used
    689             // hence nothing gets cached by RS
    690             if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
    691                 mRs.clear();
    692                 ALOGE("blur RS failed to init");
    693             } else {
    694                 mRsElement = RSC::Element::A_8(mRs);
    695                 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
    696             }
    697         }
    698         if (mRs != nullptr) {
    699             RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
    700             RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(
    701                     mRs, t, RS_ALLOCATION_MIPMAP_NONE,
    702                     RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
    703             RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(
    704                     mRs, t, RS_ALLOCATION_MIPMAP_NONE,
    705                     RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
    706 
    707             mRsScript->setRadius(radius);
    708             mRsScript->setInput(ain);
    709             mRsScript->forEach(aout);
    710 
    711             // replace the original image's pointer, avoiding a copy back to the original buffer
    712             free(*image);
    713             *image = outImage;
    714 
    715             return;
    716         }
    717     }
    718 
    719     std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
    720     Blur::generateGaussianWeights(gaussian.get(), radius);
    721 
    722     std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
    723     Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
    724     Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
    725 }
    726 
    727 static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
    728     uint32_t size = 0;
    729     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    730         CacheTexture* cacheTexture = cacheTextures[i];
    731         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    732             size += cacheTexture->getPixelBuffer()->getSize();
    733         }
    734     }
    735     return size;
    736 }
    737 
    738 static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
    739     uint32_t size = 0;
    740     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    741         CacheTexture* cacheTexture = cacheTextures[i];
    742         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    743             size += cacheTexture->calculateFreeMemory();
    744         }
    745     }
    746     return size;
    747 }
    748 
    749 const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const {
    750     switch (format) {
    751         case GL_ALPHA: {
    752             return mACacheTextures;
    753         }
    754         case GL_RGBA: {
    755             return mRGBACacheTextures;
    756         }
    757         default: {
    758             LOG_ALWAYS_FATAL("Unsupported format: %d", format);
    759             // Impossible to hit this, but the compiler doesn't know that
    760             return *(new std::vector<CacheTexture*>());
    761         }
    762     }
    763 }
    764 
    765 static void dumpTextures(String8& log, const char* tag,
    766                          const std::vector<CacheTexture*>& cacheTextures) {
    767     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    768         CacheTexture* cacheTexture = cacheTextures[i];
    769         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    770             uint32_t free = cacheTexture->calculateFreeMemory();
    771             uint32_t total = cacheTexture->getPixelBuffer()->getSize();
    772             log.appendFormat("    %-4s texture %d     %8d / %8d\n", tag, i, total - free, total);
    773         }
    774     }
    775 }
    776 
    777 void FontRenderer::dumpMemoryUsage(String8& log) const {
    778     const uint32_t sizeA8 = getCacheSize(GL_ALPHA);
    779     const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA);
    780     const uint32_t sizeRGBA = getCacheSize(GL_RGBA);
    781     const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA);
    782     log.appendFormat("  FontRenderer A8      %8d / %8d\n", usedA8, sizeA8);
    783     dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA));
    784     log.appendFormat("  FontRenderer RGBA    %8d / %8d\n", usedRGBA, sizeRGBA);
    785     dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA));
    786     log.appendFormat("  FontRenderer total   %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA);
    787 }
    788 
    789 uint32_t FontRenderer::getCacheSize(GLenum format) const {
    790     return calculateCacheSize(cacheTexturesForFormat(format));
    791 }
    792 
    793 uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
    794     return calculateFreeCacheSize(cacheTexturesForFormat(format));
    795 }
    796 
    797 uint32_t FontRenderer::getSize() const {
    798     return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
    799 }
    800 
    801 };  // namespace uirenderer
    802 };  // namespace android
    803