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