FontRenderer.cpp revision f3754a84289680782b2c7caac30fda18207a5c23
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())
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
462    // Iterate over all the cache textures and see which ones need to be updated
463    checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
464    checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
465
466    // Unbind any PBO we might have used to update textures
467    caches.pixelBufferState().unbind();
468
469    // Reset to default unpack row length to avoid affecting texture
470    // uploads in other parts of the renderer
471    if (resetPixelStore) {
472        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
473    }
474
475    mUploadTexture = false;
476}
477
478void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) {
479    if (!mFunctor) return;
480
481    bool first = true;
482    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
483        CacheTexture* texture = cacheTextures[i];
484        if (texture->canDraw()) {
485            if (first) {
486                checkTextureUpdate();
487                first = false;
488                mDrawn = true;
489            }
490
491            mFunctor->draw(*texture, mLinearFiltering);
492
493            texture->resetMesh();
494        }
495    }
496}
497
498void FontRenderer::issueDrawCommand() {
499    issueDrawCommand(mACacheTextures);
500    issueDrawCommand(mRGBACacheTextures);
501}
502
503void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
504        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
505        float x4, float y4, float u4, float v4, CacheTexture* texture) {
506    if (texture != mCurrentCacheTexture) {
507        // Now use the new texture id
508        mCurrentCacheTexture = texture;
509    }
510
511    mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
512            x3, y3, u3, v3, x4, y4, u4, v4);
513}
514
515void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
516        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
517        float x4, float y4, float u4, float v4, CacheTexture* texture) {
518
519    if (mClip &&
520            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
521        return;
522    }
523
524    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
525
526    if (mBounds) {
527        mBounds->left = std::min(mBounds->left, x1);
528        mBounds->top = std::min(mBounds->top, y3);
529        mBounds->right = std::max(mBounds->right, x3);
530        mBounds->bottom = std::max(mBounds->bottom, y1);
531    }
532
533    if (mCurrentCacheTexture->endOfMesh()) {
534        issueDrawCommand();
535    }
536}
537
538void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
539        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
540        float x4, float y4, float u4, float v4, CacheTexture* texture) {
541
542    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
543
544    if (mBounds) {
545        mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4))));
546        mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4))));
547        mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4))));
548        mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4))));
549    }
550
551    if (mCurrentCacheTexture->endOfMesh()) {
552        issueDrawCommand();
553    }
554}
555
556void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
557    mCurrentFont = Font::create(this, paint, matrix);
558}
559
560FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
561        int numGlyphs, float radius, const float* positions) {
562    checkInit();
563
564    DropShadow image;
565    image.width = 0;
566    image.height = 0;
567    image.image = nullptr;
568    image.penX = 0;
569    image.penY = 0;
570
571    if (!mCurrentFont) {
572        return image;
573    }
574
575    mDrawn = false;
576    mClip = nullptr;
577    mBounds = nullptr;
578
579    Rect bounds;
580    mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
581
582    uint32_t intRadius = Blur::convertRadiusToInt(radius);
583    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
584    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
585
586    uint32_t maxSize = Caches::getInstance().maxTextureSize;
587    if (paddedWidth > maxSize || paddedHeight > maxSize) {
588        return image;
589    }
590
591#ifdef ANDROID_ENABLE_RENDERSCRIPT
592    // Align buffers for renderscript usage
593    if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
594        paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
595    }
596    int size = paddedWidth * paddedHeight;
597    uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
598#else
599    int size = paddedWidth * paddedHeight;
600    uint8_t* dataBuffer = (uint8_t*) malloc(size);
601#endif
602
603    memset(dataBuffer, 0, size);
604
605    int penX = intRadius - bounds.left;
606    int penY = intRadius - bounds.bottom;
607
608    if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
609        // text has non-whitespace, so draw and blur to create the shadow
610        // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
611        // TODO: don't draw pure whitespace in the first place, and avoid needing this check
612        mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
613                Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
614
615        // Unbind any PBO we might have used
616        Caches::getInstance().pixelBufferState().unbind();
617
618        blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
619    }
620
621    image.width = paddedWidth;
622    image.height = paddedHeight;
623    image.image = dataBuffer;
624    image.penX = penX;
625    image.penY = penY;
626
627    return image;
628}
629
630void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
631    checkInit();
632
633    mDrawn = false;
634    mBounds = bounds;
635    mFunctor = functor;
636    mClip = clip;
637}
638
639void FontRenderer::finishRender() {
640    mBounds = nullptr;
641    mClip = nullptr;
642
643    issueDrawCommand();
644}
645
646void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
647        const SkMatrix& matrix) {
648    Font* font = Font::create(this, paint, matrix);
649    font->precache(paint, glyphs, numGlyphs);
650}
651
652void FontRenderer::endPrecaching() {
653    checkTextureUpdate();
654}
655
656bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
657        int numGlyphs, int x, int y, const float* positions,
658        Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
659    if (!mCurrentFont) {
660        ALOGE("No font set");
661        return false;
662    }
663
664    initRender(clip, bounds, functor);
665    mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
666
667    if (forceFinish) {
668        finishRender();
669    }
670
671    return mDrawn;
672}
673
674bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
675        int numGlyphs, const SkPath* path, float hOffset, float vOffset,
676        Rect* bounds, TextDrawFunctor* functor) {
677    if (!mCurrentFont) {
678        ALOGE("No font set");
679        return false;
680    }
681
682    initRender(clip, bounds, functor);
683    mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
684    finishRender();
685
686    return mDrawn;
687}
688
689void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
690    uint32_t intRadius = Blur::convertRadiusToInt(radius);
691#ifdef ANDROID_ENABLE_RENDERSCRIPT
692    if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
693        uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
694
695        if (mRs == nullptr) {
696            mRs = new RSC::RS();
697            // a null path is OK because there are no custom kernels used
698            // hence nothing gets cached by RS
699            if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
700                mRs.clear();
701                ALOGE("blur RS failed to init");
702            } else {
703                mRsElement = RSC::Element::A_8(mRs);
704                mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
705            }
706        }
707        if (mRs != nullptr) {
708            RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
709            RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
710                    RS_ALLOCATION_MIPMAP_NONE,
711                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
712                    *image);
713            RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t,
714                    RS_ALLOCATION_MIPMAP_NONE,
715                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
716                    outImage);
717
718            mRsScript->setRadius(radius);
719            mRsScript->setInput(ain);
720            mRsScript->forEach(aout);
721
722            // replace the original image's pointer, avoiding a copy back to the original buffer
723            free(*image);
724            *image = outImage;
725
726            return;
727        }
728    }
729#endif
730
731    std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
732    Blur::generateGaussianWeights(gaussian.get(), radius);
733
734    std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
735    Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
736    Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
737}
738
739static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
740    uint32_t size = 0;
741    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
742        CacheTexture* cacheTexture = cacheTextures[i];
743        if (cacheTexture && cacheTexture->getPixelBuffer()) {
744            size += cacheTexture->getPixelBuffer()->getSize();
745        }
746    }
747    return size;
748}
749
750uint32_t FontRenderer::getCacheSize(GLenum format) const {
751    switch (format) {
752        case GL_ALPHA: {
753            return calculateCacheSize(mACacheTextures);
754        }
755        case GL_RGBA: {
756            return calculateCacheSize(mRGBACacheTextures);
757        }
758        default: {
759            return 0;
760        }
761    }
762}
763
764}; // namespace uirenderer
765}; // namespace android
766