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