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