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