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