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