FontRenderer.cpp revision 1e19674107e1aa2224c2b8c7d12bfa057efe80ea
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(paint->getShader());
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(paint->getShader(), 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, cacheY = 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 SkMatrix& 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, float 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 intRadius = Blur::convertRadiusToInt(radius);
617    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
618    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
619
620    uint32_t maxSize = Caches::getInstance().maxTextureSize;
621    if (paddedWidth > maxSize || paddedHeight > maxSize) {
622        return image;
623    }
624
625#ifdef ANDROID_ENABLE_RENDERSCRIPT
626    // Align buffers for renderscript usage
627    if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
628        paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
629    }
630    int size = paddedWidth * paddedHeight;
631    uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
632#else
633    int size = paddedWidth * paddedHeight;
634    uint8_t* dataBuffer = (uint8_t*) malloc(size);
635#endif
636
637    memset(dataBuffer, 0, size);
638
639    int penX = intRadius - bounds.left;
640    int penY = intRadius - bounds.bottom;
641
642    if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
643        // text has non-whitespace, so draw and blur to create the shadow
644        // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
645        // TODO: don't draw pure whitespace in the first place, and avoid needing this check
646        mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
647                Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
648
649        // Unbind any PBO we might have used
650        Caches::getInstance().unbindPixelBuffer();
651
652        blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
653    }
654
655    image.width = paddedWidth;
656    image.height = paddedHeight;
657    image.image = dataBuffer;
658    image.penX = penX;
659    image.penY = penY;
660
661    return image;
662}
663
664void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
665    checkInit();
666
667    mDrawn = false;
668    mBounds = bounds;
669    mFunctor = functor;
670    mClip = clip;
671}
672
673void FontRenderer::finishRender() {
674    mBounds = NULL;
675    mClip = NULL;
676
677    issueDrawCommand();
678}
679
680void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs,
681        const SkMatrix& matrix) {
682    Font* font = Font::create(this, paint, matrix);
683    font->precache(paint, text, numGlyphs);
684}
685
686void FontRenderer::endPrecaching() {
687    checkTextureUpdate();
688}
689
690bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
691        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
692        const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
693    if (!mCurrentFont) {
694        ALOGE("No font set");
695        return false;
696    }
697
698    initRender(clip, bounds, functor);
699    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
700
701    if (forceFinish) {
702        finishRender();
703    }
704
705    return mDrawn;
706}
707
708bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
709        uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
710        float hOffset, float vOffset, Rect* bounds, Functor* functor) {
711    if (!mCurrentFont) {
712        ALOGE("No font set");
713        return false;
714    }
715
716    initRender(clip, bounds, functor);
717    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
718    finishRender();
719
720    return mDrawn;
721}
722
723void FontRenderer::removeFont(const Font* font) {
724    mActiveFonts.remove(font->getDescription());
725
726    if (mCurrentFont == font) {
727        mCurrentFont = NULL;
728    }
729}
730
731void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
732    uint32_t intRadius = Blur::convertRadiusToInt(radius);
733#ifdef ANDROID_ENABLE_RENDERSCRIPT
734    if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
735        uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
736
737        if (mRs == 0) {
738            mRs = new RSC::RS();
739            // a null path is OK because there are no custom kernels used
740            // hence nothing gets cached by RS
741            if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
742                mRs.clear();
743                ALOGE("blur RS failed to init");
744            } else {
745                mRsElement = RSC::Element::A_8(mRs);
746                mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
747            }
748        }
749        if (mRs != 0) {
750            RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
751            RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
752                    RS_ALLOCATION_MIPMAP_NONE,
753                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
754                    *image);
755            RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t,
756                    RS_ALLOCATION_MIPMAP_NONE,
757                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
758                    outImage);
759
760            mRsScript->setRadius(radius);
761            mRsScript->setInput(ain);
762            mRsScript->forEach(aout);
763
764            // replace the original image's pointer, avoiding a copy back to the original buffer
765            free(*image);
766            *image = outImage;
767
768            return;
769        }
770    }
771#endif
772
773    float *gaussian = new float[2 * intRadius + 1];
774    Blur::generateGaussianWeights(gaussian, intRadius);
775
776    uint8_t* scratch = new uint8_t[width * height];
777    Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
778    Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
779
780    delete[] gaussian;
781    delete[] scratch;
782}
783
784static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) {
785    uint32_t size = 0;
786    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
787        CacheTexture* cacheTexture = cacheTextures[i];
788        if (cacheTexture && cacheTexture->getPixelBuffer()) {
789            size += cacheTexture->getPixelBuffer()->getSize();
790        }
791    }
792    return size;
793}
794
795uint32_t FontRenderer::getCacheSize(GLenum format) const {
796    switch (format) {
797        case GL_ALPHA: {
798            return calculateCacheSize(mACacheTextures);
799        }
800        case GL_RGBA: {
801            return calculateCacheSize(mRGBACacheTextures);
802        }
803        default: {
804            return 0;
805        }
806    }
807}
808
809}; // namespace uirenderer
810}; // namespace android
811