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