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