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