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