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