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