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 "font/Font.h"
     26 #include "Glop.h"
     27 #include "GlopBuilder.h"
     28 #include "PixelBuffer.h"
     29 #include "Rect.h"
     30 #include "renderstate/RenderState.h"
     31 #include "utils/Blur.h"
     32 #include "utils/Timing.h"
     33 
     34 #include <algorithm>
     35 #include <RenderScript.h>
     36 #include <SkGlyph.h>
     37 #include <SkUtils.h>
     38 #include <utils/Log.h>
     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 = pureTranslate
     59             ? 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 
     97     if (sLogFontRendererCreate) {
     98         INIT_LOGD("Creating FontRenderer");
     99     }
    100 
    101     auto deviceInfo = DeviceInfo::get();
    102     auto displayInfo = deviceInfo->displayInfo();
    103     int maxTextureSize = deviceInfo->maxTextureSize();
    104 
    105     // Adjust cache size based on Pixel's desnsity.
    106     constexpr float PIXEL_DENSITY = 2.6;
    107     const float densityRatio = displayInfo.density / PIXEL_DENSITY;
    108 
    109     // TODO: Most devices are hardcoded with this configuration, does it need to be dynamic?
    110     mSmallCacheWidth =
    111             OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
    112     mSmallCacheHeight =
    113             OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
    114     mLargeCacheWidth =
    115             OffscreenBuffer::computeIdealDimension(std::min(2048, maxTextureSize) * densityRatio);
    116     mLargeCacheHeight =
    117             OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
    118 
    119     if (sLogFontRendererCreate) {
    120         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
    121                 mSmallCacheWidth, mSmallCacheHeight,
    122                 mLargeCacheWidth, mLargeCacheHeight >> 1,
    123                 mLargeCacheWidth, mLargeCacheHeight >> 1,
    124                 mLargeCacheWidth, mLargeCacheHeight);
    125     }
    126 
    127     sLogFontRendererCreate = false;
    128 }
    129 
    130 void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) {
    131     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    132         delete cacheTextures[i];
    133     }
    134     cacheTextures.clear();
    135 }
    136 
    137 FontRenderer::~FontRenderer() {
    138     clearCacheTextures(mACacheTextures);
    139     clearCacheTextures(mRGBACacheTextures);
    140 
    141     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    142     while (it.next()) {
    143         delete it.value();
    144     }
    145     mActiveFonts.clear();
    146 }
    147 
    148 void FontRenderer::flushAllAndInvalidate() {
    149     issueDrawCommand();
    150 
    151     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    152     while (it.next()) {
    153         it.value()->invalidateTextureCache();
    154     }
    155 
    156     for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
    157         mACacheTextures[i]->init();
    158 
    159 #ifdef BUGREPORT_FONT_CACHE_USAGE
    160         mHistoryTracker.glyphsCleared(mACacheTextures[i]);
    161 #endif
    162     }
    163 
    164     for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
    165         mRGBACacheTextures[i]->init();
    166 #ifdef BUGREPORT_FONT_CACHE_USAGE
    167         mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]);
    168 #endif
    169     }
    170 
    171     mDrawn = false;
    172 }
    173 
    174 void FontRenderer::flushLargeCaches(std::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 #ifdef BUGREPORT_FONT_CACHE_USAGE
    181             mHistoryTracker.glyphsCleared(cacheTexture);
    182 #endif
    183             LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    184             while (it.next()) {
    185                 it.value()->invalidateTextureCache(cacheTexture);
    186             }
    187             cacheTexture->releasePixelBuffer();
    188         }
    189     }
    190 }
    191 
    192 void FontRenderer::flushLargeCaches() {
    193     flushLargeCaches(mACacheTextures);
    194     flushLargeCaches(mRGBACacheTextures);
    195 }
    196 
    197 CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures,
    198         const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
    199     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    200         if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
    201             return cacheTextures[i];
    202         }
    203     }
    204     // Could not fit glyph into current cache textures
    205     return nullptr;
    206 }
    207 
    208 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
    209         uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
    210     checkInit();
    211 
    212     // If the glyph bitmap is empty let's assum the glyph is valid
    213     // so we can avoid doing extra work later on
    214     if (glyph.fWidth == 0 || glyph.fHeight == 0) {
    215         cachedGlyph->mIsValid = true;
    216         cachedGlyph->mCacheTexture = nullptr;
    217         return;
    218     }
    219 
    220     cachedGlyph->mIsValid = false;
    221 
    222     // choose an appropriate cache texture list for this glyph format
    223     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
    224     std::vector<CacheTexture*>* cacheTextures = nullptr;
    225     switch (format) {
    226         case SkMask::kA8_Format:
    227         case SkMask::kBW_Format:
    228             cacheTextures = &mACacheTextures;
    229             break;
    230         case SkMask::kARGB32_Format:
    231             cacheTextures = &mRGBACacheTextures;
    232             break;
    233         default:
    234 #if DEBUG_FONT_RENDERER
    235             ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
    236 #endif
    237         return;
    238     }
    239 
    240     // If the glyph is too tall, don't cache it
    241     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
    242                 (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
    243         ALOGE("Font size too large to fit in cache. width, height = %i, %i",
    244                 (int) glyph.fWidth, (int) glyph.fHeight);
    245         return;
    246     }
    247 
    248     // Now copy the bitmap into the cache texture
    249     uint32_t startX = 0;
    250     uint32_t startY = 0;
    251 
    252     CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
    253 
    254     if (!cacheTexture) {
    255         if (!precaching) {
    256             // If the new glyph didn't fit and we are not just trying to precache it,
    257             // clear out the cache and try again
    258             flushAllAndInvalidate();
    259             cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
    260         }
    261 
    262         if (!cacheTexture) {
    263             // either the glyph didn't fit or we're precaching and will cache it when we draw
    264             return;
    265         }
    266     }
    267 
    268     cachedGlyph->mCacheTexture = cacheTexture;
    269 
    270     *retOriginX = startX;
    271     *retOriginY = startY;
    272 
    273     uint32_t endX = startX + glyph.fWidth;
    274     uint32_t endY = startY + glyph.fHeight;
    275 
    276     uint32_t cacheWidth = cacheTexture->getWidth();
    277 
    278     if (!cacheTexture->getPixelBuffer()) {
    279         Caches::getInstance().textureState().activateTexture(0);
    280         // Large-glyph texture memory is allocated only as needed
    281         cacheTexture->allocatePixelBuffer();
    282     }
    283     if (!cacheTexture->mesh()) {
    284         cacheTexture->allocateMesh();
    285     }
    286 
    287     uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
    288     uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
    289     int srcStride = glyph.rowBytes();
    290 
    291     // Copy the glyph image, taking the mask format into account
    292     switch (format) {
    293         case SkMask::kA8_Format: {
    294             uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
    295                     - TEXTURE_BORDER_SIZE;
    296             // write leading border line
    297             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    298             // write glyph data
    299             if (mGammaTable) {
    300                 for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
    301                     row = cacheY * cacheWidth;
    302                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    303                     for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
    304                         uint8_t tempCol = bitmapBuffer[bY + bX];
    305                         cacheBuffer[row + cacheX] = mGammaTable[tempCol];
    306                     }
    307                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    308                 }
    309             } else {
    310                 for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
    311                     row = cacheY * cacheWidth;
    312                     memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
    313                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    314                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    315                 }
    316             }
    317             // write trailing border line
    318             row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    319             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    320             break;
    321         }
    322         case SkMask::kARGB32_Format: {
    323             // prep data lengths
    324             const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
    325             const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
    326             size_t rowSize = formatSize * glyph.fWidth;
    327             // prep advances
    328             size_t dstStride = formatSize * cacheWidth;
    329             // prep indices
    330             // - we actually start one row early, and then increment before first copy
    331             uint8_t* src = &bitmapBuffer[0 - srcStride];
    332             uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
    333             uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
    334             uint8_t* dstL = dst - borderSize;
    335             uint8_t* dstR = dst + rowSize;
    336             // write leading border line
    337             memset(dstL, 0, rowSize + 2 * borderSize);
    338             // write glyph data
    339             while (dst < dstEnd) {
    340                 memset(dstL += dstStride, 0, borderSize); // leading border column
    341                 memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
    342                 memset(dstR += dstStride, 0, borderSize); // trailing border column
    343             }
    344             // write trailing border line
    345             memset(dstL += dstStride, 0, rowSize + 2 * borderSize);
    346             break;
    347         }
    348         case SkMask::kBW_Format: {
    349             uint32_t cacheX = 0, cacheY = 0;
    350             uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
    351                     - TEXTURE_BORDER_SIZE;
    352             static const uint8_t COLORS[2] = { 0, 255 };
    353             // write leading border line
    354             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    355             // write glyph data
    356             for (cacheY = startY; cacheY < endY; cacheY++) {
    357                 cacheX = startX;
    358                 int rowBytes = srcStride;
    359                 uint8_t* buffer = bitmapBuffer;
    360 
    361                 row = cacheY * cacheWidth;
    362                 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    363                 while (--rowBytes >= 0) {
    364                     uint8_t b = *buffer++;
    365                     for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
    366                         cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
    367                     }
    368                 }
    369                 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    370 
    371                 bitmapBuffer += srcStride;
    372             }
    373             // write trailing border line
    374             row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    375             memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    376             break;
    377         }
    378         default:
    379             ALOGW("Unknown glyph format: 0x%x", format);
    380             break;
    381     }
    382 
    383     cachedGlyph->mIsValid = true;
    384 
    385 #ifdef BUGREPORT_FONT_CACHE_USAGE
    386     mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight);
    387 #endif
    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     // Align buffers for renderscript usage
    592     if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
    593         paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
    594     }
    595     int size = paddedWidth * paddedHeight;
    596     uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
    597 
    598     memset(dataBuffer, 0, size);
    599 
    600     int penX = intRadius - bounds.left;
    601     int penY = intRadius - bounds.bottom;
    602 
    603     if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
    604         // text has non-whitespace, so draw and blur to create the shadow
    605         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
    606         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
    607         mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
    608                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
    609 
    610         // Unbind any PBO we might have used
    611         Caches::getInstance().pixelBufferState().unbind();
    612 
    613         blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
    614     }
    615 
    616     image.width = paddedWidth;
    617     image.height = paddedHeight;
    618     image.image = dataBuffer;
    619     image.penX = penX;
    620     image.penY = penY;
    621 
    622     return image;
    623 }
    624 
    625 void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
    626     checkInit();
    627 
    628     mDrawn = false;
    629     mBounds = bounds;
    630     mFunctor = functor;
    631     mClip = clip;
    632 }
    633 
    634 void FontRenderer::finishRender() {
    635     mBounds = nullptr;
    636     mClip = nullptr;
    637 
    638     issueDrawCommand();
    639 }
    640 
    641 void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
    642         const SkMatrix& matrix) {
    643     Font* font = Font::create(this, paint, matrix);
    644     font->precache(paint, glyphs, numGlyphs);
    645 }
    646 
    647 void FontRenderer::endPrecaching() {
    648     checkTextureUpdate();
    649 }
    650 
    651 bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
    652         int numGlyphs, int x, int y, const float* positions,
    653         Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
    654     if (!mCurrentFont) {
    655         ALOGE("No font set");
    656         return false;
    657     }
    658 
    659     initRender(clip, bounds, functor);
    660     mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
    661 
    662     if (forceFinish) {
    663         finishRender();
    664     }
    665 
    666     return mDrawn;
    667 }
    668 
    669 bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
    670         int numGlyphs, const SkPath* path, float hOffset, float vOffset,
    671         Rect* bounds, TextDrawFunctor* functor) {
    672     if (!mCurrentFont) {
    673         ALOGE("No font set");
    674         return false;
    675     }
    676 
    677     initRender(clip, bounds, functor);
    678     mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
    679     finishRender();
    680 
    681     return mDrawn;
    682 }
    683 
    684 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
    685     uint32_t intRadius = Blur::convertRadiusToInt(radius);
    686     if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
    687         uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
    688 
    689         if (mRs == nullptr) {
    690             mRs = new RSC::RS();
    691             // a null path is OK because there are no custom kernels used
    692             // hence nothing gets cached by RS
    693             if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
    694                 mRs.clear();
    695                 ALOGE("blur RS failed to init");
    696             } else {
    697                 mRsElement = RSC::Element::A_8(mRs);
    698                 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
    699             }
    700         }
    701         if (mRs != nullptr) {
    702             RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
    703             RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
    704                     RS_ALLOCATION_MIPMAP_NONE,
    705                     RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
    706                     *image);
    707             RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t,
    708                     RS_ALLOCATION_MIPMAP_NONE,
    709                     RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
    710                     outImage);
    711 
    712             mRsScript->setRadius(radius);
    713             mRsScript->setInput(ain);
    714             mRsScript->forEach(aout);
    715 
    716             // replace the original image's pointer, avoiding a copy back to the original buffer
    717             free(*image);
    718             *image = outImage;
    719 
    720             return;
    721         }
    722     }
    723 
    724     std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
    725     Blur::generateGaussianWeights(gaussian.get(), radius);
    726 
    727     std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
    728     Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
    729     Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
    730 }
    731 
    732 static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
    733     uint32_t size = 0;
    734     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    735         CacheTexture* cacheTexture = cacheTextures[i];
    736         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    737             size += cacheTexture->getPixelBuffer()->getSize();
    738         }
    739     }
    740     return size;
    741 }
    742 
    743 static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
    744     uint32_t size = 0;
    745     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    746         CacheTexture* cacheTexture = cacheTextures[i];
    747         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    748             size += cacheTexture->calculateFreeMemory();
    749         }
    750     }
    751     return size;
    752 }
    753 
    754 const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const {
    755     switch (format) {
    756         case GL_ALPHA: {
    757             return mACacheTextures;
    758         }
    759         case GL_RGBA: {
    760             return mRGBACacheTextures;
    761         }
    762         default: {
    763             LOG_ALWAYS_FATAL("Unsupported format: %d", format);
    764             // Impossible to hit this, but the compiler doesn't know that
    765             return *(new std::vector<CacheTexture*>());
    766         }
    767     }
    768 }
    769 
    770 static void dumpTextures(String8& log, const char* tag,
    771         const std::vector<CacheTexture*>& cacheTextures) {
    772     for (uint32_t i = 0; i < cacheTextures.size(); i++) {
    773         CacheTexture* cacheTexture = cacheTextures[i];
    774         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    775             uint32_t free = cacheTexture->calculateFreeMemory();
    776             uint32_t total = cacheTexture->getPixelBuffer()->getSize();
    777             log.appendFormat("    %-4s texture %d     %8d / %8d\n", tag, i, total - free, total);
    778         }
    779     }
    780 }
    781 
    782 void FontRenderer::dumpMemoryUsage(String8& log) const {
    783     const uint32_t sizeA8 = getCacheSize(GL_ALPHA);
    784     const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA);
    785     const uint32_t sizeRGBA = getCacheSize(GL_RGBA);
    786     const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA);
    787     log.appendFormat("  FontRenderer A8      %8d / %8d\n", usedA8, sizeA8);
    788     dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA));
    789     log.appendFormat("  FontRenderer RGBA    %8d / %8d\n", usedRGBA, sizeRGBA);
    790     dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA));
    791     log.appendFormat("  FontRenderer total   %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA);
    792 }
    793 
    794 uint32_t FontRenderer::getCacheSize(GLenum format) const {
    795     return calculateCacheSize(cacheTexturesForFormat(format));
    796 }
    797 
    798 uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
    799     return calculateFreeCacheSize(cacheTexturesForFormat(format));
    800 }
    801 
    802 uint32_t FontRenderer::getSize() const {
    803     return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
    804 }
    805 
    806 }; // namespace uirenderer
    807 }; // namespace android
    808