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