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/Functor.h>
     25 #include <utils/Log.h>
     26 
     27 #include <RenderScript.h>
     28 
     29 #include "utils/Blur.h"
     30 #include "utils/Timing.h"
     31 
     32 #include "Caches.h"
     33 #include "Debug.h"
     34 #include "Extensions.h"
     35 #include "FontRenderer.h"
     36 #include "PixelBuffer.h"
     37 #include "Rect.h"
     38 
     39 namespace android {
     40 namespace uirenderer {
     41 
     42 // blur inputs smaller than this constant will bypass renderscript
     43 #define RS_MIN_INPUT_CUTOFF 10000
     44 
     45 ///////////////////////////////////////////////////////////////////////////////
     46 // FontRenderer
     47 ///////////////////////////////////////////////////////////////////////////////
     48 
     49 static bool sLogFontRendererCreate = true;
     50 
     51 FontRenderer::FontRenderer() :
     52         mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
     53 
     54     if (sLogFontRendererCreate) {
     55         INIT_LOGD("Creating FontRenderer");
     56     }
     57 
     58     mGammaTable = NULL;
     59     mInitialized = false;
     60 
     61     mCurrentCacheTexture = NULL;
     62 
     63     mLinearFiltering = false;
     64 
     65     mIndexBufferID = 0;
     66 
     67     mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
     68     mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
     69     mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
     70     mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
     71 
     72     char property[PROPERTY_VALUE_MAX];
     73     if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
     74         mSmallCacheWidth = atoi(property);
     75     }
     76 
     77     if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
     78         mSmallCacheHeight = atoi(property);
     79     }
     80 
     81     if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
     82         mLargeCacheWidth = atoi(property);
     83     }
     84 
     85     if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
     86         mLargeCacheHeight = atoi(property);
     87     }
     88 
     89     uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
     90     mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
     91     mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
     92     mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
     93     mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
     94 
     95     if (sLogFontRendererCreate) {
     96         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
     97                 mSmallCacheWidth, mSmallCacheHeight,
     98                 mLargeCacheWidth, mLargeCacheHeight >> 1,
     99                 mLargeCacheWidth, mLargeCacheHeight >> 1,
    100                 mLargeCacheWidth, mLargeCacheHeight);
    101     }
    102 
    103     sLogFontRendererCreate = false;
    104 }
    105 
    106 FontRenderer::~FontRenderer() {
    107     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    108         delete mCacheTextures[i];
    109     }
    110     mCacheTextures.clear();
    111 
    112     if (mInitialized) {
    113         // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
    114         Caches::getInstance().unbindIndicesBuffer();
    115         glDeleteBuffers(1, &mIndexBufferID);
    116     }
    117 
    118     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    119     while (it.next()) {
    120         delete it.value();
    121     }
    122     mActiveFonts.clear();
    123 }
    124 
    125 void FontRenderer::flushAllAndInvalidate() {
    126     issueDrawCommand();
    127 
    128     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    129     while (it.next()) {
    130         it.value()->invalidateTextureCache();
    131     }
    132 
    133     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    134         mCacheTextures[i]->init();
    135     }
    136 }
    137 
    138 void FontRenderer::flushLargeCaches() {
    139     // Start from 1; don't deallocate smallest/default texture
    140     for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
    141         CacheTexture* cacheTexture = mCacheTextures[i];
    142         if (cacheTexture->getPixelBuffer()) {
    143             cacheTexture->init();
    144             LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
    145             while (it.next()) {
    146                 it.value()->invalidateTextureCache(cacheTexture);
    147             }
    148             cacheTexture->releaseTexture();
    149         }
    150     }
    151 }
    152 
    153 CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
    154         uint32_t* startX, uint32_t* startY) {
    155     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    156         if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
    157             return mCacheTextures[i];
    158         }
    159     }
    160     // Could not fit glyph into current cache textures
    161     return NULL;
    162 }
    163 
    164 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
    165         uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
    166     checkInit();
    167 
    168     // If the glyph bitmap is empty let's assum the glyph is valid
    169     // so we can avoid doing extra work later on
    170     if (glyph.fWidth == 0 || glyph.fHeight == 0) {
    171         cachedGlyph->mIsValid = true;
    172         cachedGlyph->mCacheTexture = NULL;
    173         return;
    174     }
    175 
    176     cachedGlyph->mIsValid = false;
    177 
    178     // If the glyph is too tall, don't cache it
    179     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
    180                 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
    181         ALOGE("Font size too large to fit in cache. width, height = %i, %i",
    182                 (int) glyph.fWidth, (int) glyph.fHeight);
    183         return;
    184     }
    185 
    186     // Now copy the bitmap into the cache texture
    187     uint32_t startX = 0;
    188     uint32_t startY = 0;
    189 
    190     CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
    191 
    192     if (!cacheTexture) {
    193         if (!precaching) {
    194             // If the new glyph didn't fit and we are not just trying to precache it,
    195             // clear out the cache and try again
    196             flushAllAndInvalidate();
    197             cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
    198         }
    199 
    200         if (!cacheTexture) {
    201             // either the glyph didn't fit or we're precaching and will cache it when we draw
    202             return;
    203         }
    204     }
    205 
    206     cachedGlyph->mCacheTexture = cacheTexture;
    207 
    208     *retOriginX = startX;
    209     *retOriginY = startY;
    210 
    211     uint32_t endX = startX + glyph.fWidth;
    212     uint32_t endY = startY + glyph.fHeight;
    213 
    214     uint32_t cacheWidth = cacheTexture->getWidth();
    215 
    216     if (!cacheTexture->getPixelBuffer()) {
    217         Caches::getInstance().activeTexture(0);
    218         // Large-glyph texture memory is allocated only as needed
    219         cacheTexture->allocateTexture();
    220     }
    221     if (!cacheTexture->mesh()) {
    222         cacheTexture->allocateMesh();
    223     }
    224 
    225     // Tells us whether the glyphs is B&W (1 bit per pixel)
    226     // or anti-aliased (8 bits per pixel)
    227     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
    228 
    229     uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
    230     uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
    231 
    232     // Copy the glyph image, taking the mask format into account
    233     uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
    234     int stride = glyph.rowBytes();
    235 
    236     uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    237     memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    238 
    239     switch (format) {
    240         case SkMask::kA8_Format: {
    241             if (mGammaTable) {
    242                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
    243                     row = cacheY * cacheWidth;
    244                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    245                     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
    246                         uint8_t tempCol = bitmapBuffer[bY + bX];
    247                         cacheBuffer[row + cacheX] = mGammaTable[tempCol];
    248                     }
    249                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    250                 }
    251             } else {
    252                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
    253                     row = cacheY * cacheWidth;
    254                     memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
    255                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    256                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    257                 }
    258             }
    259             break;
    260         }
    261         case SkMask::kBW_Format: {
    262             static const uint8_t COLORS[2] = { 0, 255 };
    263 
    264             for (cacheY = startY; cacheY < endY; cacheY++) {
    265                 cacheX = startX;
    266                 int rowBytes = stride;
    267                 uint8_t* buffer = bitmapBuffer;
    268 
    269                 row = cacheY * cacheWidth;
    270                 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
    271                 while (--rowBytes >= 0) {
    272                     uint8_t b = *buffer++;
    273                     for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
    274                         cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
    275                     }
    276                 }
    277                 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
    278 
    279                 bitmapBuffer += stride;
    280             }
    281             break;
    282         }
    283         default:
    284             ALOGW("Unkown glyph format: 0x%x", format);
    285             break;
    286     }
    287 
    288     row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
    289     memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
    290 
    291     cachedGlyph->mIsValid = true;
    292 }
    293 
    294 CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
    295     CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
    296 
    297     if (allocate) {
    298         Caches::getInstance().activeTexture(0);
    299         cacheTexture->allocateTexture();
    300         cacheTexture->allocateMesh();
    301     }
    302 
    303     return cacheTexture;
    304 }
    305 
    306 void FontRenderer::initTextTexture() {
    307     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    308         delete mCacheTextures[i];
    309     }
    310     mCacheTextures.clear();
    311 
    312     mUploadTexture = false;
    313     mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
    314     mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
    315     mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
    316     mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
    317     mCurrentCacheTexture = mCacheTextures[0];
    318 }
    319 
    320 // Avoid having to reallocate memory and render quad by quad
    321 void FontRenderer::initVertexArrayBuffers() {
    322     uint32_t numIndices = gMaxNumberOfQuads * 6;
    323     uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
    324     uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
    325 
    326     // Four verts, two triangles , six indices per quad
    327     for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
    328         int i6 = i * 6;
    329         int i4 = i * 4;
    330 
    331         indexBufferData[i6 + 0] = i4 + 0;
    332         indexBufferData[i6 + 1] = i4 + 1;
    333         indexBufferData[i6 + 2] = i4 + 2;
    334 
    335         indexBufferData[i6 + 3] = i4 + 0;
    336         indexBufferData[i6 + 4] = i4 + 2;
    337         indexBufferData[i6 + 5] = i4 + 3;
    338     }
    339 
    340     glGenBuffers(1, &mIndexBufferID);
    341     Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
    342     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
    343 
    344     free(indexBufferData);
    345 }
    346 
    347 // We don't want to allocate anything unless we actually draw text
    348 void FontRenderer::checkInit() {
    349     if (mInitialized) {
    350         return;
    351     }
    352 
    353     initTextTexture();
    354     initVertexArrayBuffers();
    355 
    356     mInitialized = true;
    357 }
    358 
    359 void FontRenderer::checkTextureUpdate() {
    360     if (!mUploadTexture) {
    361         return;
    362     }
    363 
    364     Caches& caches = Caches::getInstance();
    365     GLuint lastTextureId = 0;
    366 
    367     bool resetPixelStore = false;
    368     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    369 
    370     // Iterate over all the cache textures and see which ones need to be updated
    371     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    372         CacheTexture* cacheTexture = mCacheTextures[i];
    373         if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
    374             if (cacheTexture->getTextureId() != lastTextureId) {
    375                 lastTextureId = cacheTexture->getTextureId();
    376                 caches.activeTexture(0);
    377                 glBindTexture(GL_TEXTURE_2D, lastTextureId);
    378             }
    379 
    380             if (cacheTexture->upload()) {
    381                 resetPixelStore = true;
    382             }
    383 
    384 #if DEBUG_FONT_RENDERER
    385             ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
    386                     i, x, y, width, height);
    387 #endif
    388         }
    389     }
    390 
    391     // Unbind any PBO we might have used to update textures
    392     caches.unbindPixelBuffer();
    393 
    394     // Reset to default unpack row length to avoid affecting texture
    395     // uploads in other parts of the renderer
    396     if (resetPixelStore) {
    397         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    398     }
    399 
    400     mUploadTexture = false;
    401 }
    402 
    403 void FontRenderer::issueDrawCommand() {
    404     bool first = true;
    405     bool force = false;
    406 
    407     GLuint lastId = 0;
    408     Caches& caches = Caches::getInstance();
    409 
    410     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    411         CacheTexture* texture = mCacheTextures[i];
    412         if (texture->canDraw()) {
    413             if (first) {
    414                 if (mFunctor) (*mFunctor)(0, NULL);
    415 
    416                 checkTextureUpdate();
    417                 caches.bindIndicesBuffer(mIndexBufferID);
    418 
    419                 if (!mDrawn) {
    420                     // If returns true, a VBO was bound and we must
    421                     // rebind our vertex attrib pointers even if
    422                     // they have the same values as the current pointers
    423                     force = caches.unbindMeshBuffer();
    424                 }
    425 
    426                 caches.activeTexture(0);
    427                 first = false;
    428             }
    429 
    430             glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
    431             texture->setLinearFiltering(mLinearFiltering, false);
    432 
    433             TextureVertex* mesh = texture->mesh();
    434             caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
    435             caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
    436             force = false;
    437 
    438             glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
    439                     GL_UNSIGNED_SHORT, texture->indices());
    440 
    441             texture->resetMesh();
    442         }
    443     }
    444 
    445     mDrawn = true;
    446 }
    447 
    448 void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
    449         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
    450         float x4, float y4, float u4, float v4, CacheTexture* texture) {
    451     if (texture != mCurrentCacheTexture) {
    452         // Now use the new texture id
    453         mCurrentCacheTexture = texture;
    454     }
    455 
    456     mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
    457             x3, y3, u3, v3, x4, y4, u4, v4);
    458 }
    459 
    460 void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
    461         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
    462         float x4, float y4, float u4, float v4, CacheTexture* texture) {
    463 
    464     if (mClip &&
    465             (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
    466         return;
    467     }
    468 
    469     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
    470 
    471     if (mBounds) {
    472         mBounds->left = fmin(mBounds->left, x1);
    473         mBounds->top = fmin(mBounds->top, y3);
    474         mBounds->right = fmax(mBounds->right, x3);
    475         mBounds->bottom = fmax(mBounds->bottom, y1);
    476     }
    477 
    478     if (mCurrentCacheTexture->endOfMesh()) {
    479         issueDrawCommand();
    480     }
    481 }
    482 
    483 void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
    484         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
    485         float x4, float y4, float u4, float v4, CacheTexture* texture) {
    486 
    487     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
    488 
    489     if (mBounds) {
    490         mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
    491         mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
    492         mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
    493         mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
    494     }
    495 
    496     if (mCurrentCacheTexture->endOfMesh()) {
    497         issueDrawCommand();
    498     }
    499 }
    500 
    501 void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
    502     mCurrentFont = Font::create(this, paint, matrix);
    503 }
    504 
    505 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
    506         uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
    507     checkInit();
    508 
    509     DropShadow image;
    510     image.width = 0;
    511     image.height = 0;
    512     image.image = NULL;
    513     image.penX = 0;
    514     image.penY = 0;
    515 
    516     if (!mCurrentFont) {
    517         return image;
    518     }
    519 
    520     mDrawn = false;
    521     mClip = NULL;
    522     mBounds = NULL;
    523 
    524     Rect bounds;
    525     mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
    526 
    527     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
    528     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
    529 
    530     uint32_t maxSize = Caches::getInstance().maxTextureSize;
    531     if (paddedWidth > maxSize || paddedHeight > maxSize) {
    532         return image;
    533     }
    534 
    535     // Align buffers for renderscript usage
    536     if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
    537         paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
    538     }
    539 
    540     int size = paddedWidth * paddedHeight;
    541     uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
    542     memset(dataBuffer, 0, size);
    543 
    544     int penX = radius - bounds.left;
    545     int penY = radius - bounds.bottom;
    546 
    547     if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
    548         // text has non-whitespace, so draw and blur to create the shadow
    549         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
    550         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
    551         mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
    552                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
    553 
    554         // Unbind any PBO we might have used
    555         Caches::getInstance().unbindPixelBuffer();
    556 
    557         blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
    558     }
    559 
    560     image.width = paddedWidth;
    561     image.height = paddedHeight;
    562     image.image = dataBuffer;
    563     image.penX = penX;
    564     image.penY = penY;
    565 
    566     return image;
    567 }
    568 
    569 void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
    570     checkInit();
    571 
    572     mDrawn = false;
    573     mBounds = bounds;
    574     mFunctor = functor;
    575     mClip = clip;
    576 }
    577 
    578 void FontRenderer::finishRender() {
    579     mBounds = NULL;
    580     mClip = NULL;
    581 
    582     issueDrawCommand();
    583 }
    584 
    585 void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
    586     Font* font = Font::create(this, paint, matrix);
    587     font->precache(paint, text, numGlyphs);
    588 }
    589 
    590 void FontRenderer::endPrecaching() {
    591     checkTextureUpdate();
    592 }
    593 
    594 bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
    595         uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
    596         const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
    597     if (!mCurrentFont) {
    598         ALOGE("No font set");
    599         return false;
    600     }
    601 
    602     initRender(clip, bounds, functor);
    603     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
    604 
    605     if (forceFinish) {
    606         finishRender();
    607     }
    608 
    609     return mDrawn;
    610 }
    611 
    612 bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
    613         uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
    614         float hOffset, float vOffset, Rect* bounds) {
    615     if (!mCurrentFont) {
    616         ALOGE("No font set");
    617         return false;
    618     }
    619 
    620     initRender(clip, bounds, NULL);
    621     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
    622     finishRender();
    623 
    624     return mDrawn;
    625 }
    626 
    627 void FontRenderer::removeFont(const Font* font) {
    628     mActiveFonts.remove(font->getDescription());
    629 
    630     if (mCurrentFont == font) {
    631         mCurrentFont = NULL;
    632     }
    633 }
    634 
    635 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
    636     if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
    637         float *gaussian = new float[2 * radius + 1];
    638         Blur::generateGaussianWeights(gaussian, radius);
    639 
    640         uint8_t* scratch = new uint8_t[width * height];
    641         Blur::horizontal(gaussian, radius, *image, scratch, width, height);
    642         Blur::vertical(gaussian, radius, scratch, *image, width, height);
    643 
    644         delete[] gaussian;
    645         delete[] scratch;
    646         return;
    647     }
    648 
    649     uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
    650 
    651     if (mRs.get() == 0) {
    652         mRs = new RSC::RS();
    653         if (!mRs->init(true, true)) {
    654             ALOGE("blur RS failed to init");
    655         }
    656 
    657         mRsElement = RSC::Element::A_8(mRs);
    658         mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
    659     }
    660 
    661     sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
    662     sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
    663             RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
    664     sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
    665             RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
    666 
    667     mRsScript->setRadius(radius);
    668     mRsScript->blur(ain, aout);
    669 
    670     // replace the original image's pointer, avoiding a copy back to the original buffer
    671     free(*image);
    672     *image = outImage;
    673 }
    674 
    675 uint32_t FontRenderer::getCacheSize() const {
    676     uint32_t size = 0;
    677     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
    678         CacheTexture* cacheTexture = mCacheTextures[i];
    679         if (cacheTexture && cacheTexture->getPixelBuffer()) {
    680             size += cacheTexture->getPixelBuffer()->getSize();
    681         }
    682     }
    683     return size;
    684 }
    685 
    686 }; // namespace uirenderer
    687 }; // namespace android
    688