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