FontRenderer.cpp revision d8c8aaa82ef90f30df647ca42453e953ee52af0f
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        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, 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
397CacheTexture* 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
410void 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
431void FontRenderer::checkInit() {
432    if (mInitialized) {
433        return;
434    }
435
436    initTextTexture();
437
438    mInitialized = true;
439}
440
441void 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
459void 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
486void 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
529void FontRenderer::issueDrawCommand() {
530    issueDrawCommand(mACacheTextures);
531    issueDrawCommand(mRGBACacheTextures);
532
533    mDrawn = true;
534}
535
536void 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
548void 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
571void 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
589void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
590    mCurrentFont = Font::create(this, paint, matrix);
591}
592
593FontRenderer::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
662void 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
671void FontRenderer::finishRender() {
672    mBounds = NULL;
673    mClip = NULL;
674
675    issueDrawCommand();
676}
677
678void 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
683void FontRenderer::endPrecaching() {
684    checkTextureUpdate();
685}
686
687bool 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
705bool 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
720void FontRenderer::removeFont(const Font* font) {
721    mActiveFonts.remove(font->getDescription());
722
723    if (mCurrentFont == font) {
724        mCurrentFont = NULL;
725    }
726}
727
728void 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(true, true)) {
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
774static 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
785uint32_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