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