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