FontRenderer.cpp revision 23e500d168e8d649305445f6c1d2b49d8d2c00fd
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#include "Caches.h"
27#include "Debug.h"
28#include "FontRenderer.h"
29#include "Rect.h"
30
31namespace android {
32namespace uirenderer {
33
34///////////////////////////////////////////////////////////////////////////////
35// FontRenderer
36///////////////////////////////////////////////////////////////////////////////
37
38static bool sLogFontRendererCreate = true;
39
40FontRenderer::FontRenderer() :
41        mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
42
43    if (sLogFontRendererCreate) {
44        INIT_LOGD("Creating FontRenderer");
45    }
46
47    mGammaTable = NULL;
48    mInitialized = false;
49    mMaxNumberOfQuads = 1024;
50    mCurrentQuadIndex = 0;
51    mLastQuadIndex = 0;
52
53    mTextMesh = NULL;
54    mCurrentCacheTexture = NULL;
55
56    mLinearFiltering = false;
57
58    mIndexBufferID = 0;
59
60    mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
61    mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
62    mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
63    mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
64
65    char property[PROPERTY_VALUE_MAX];
66    if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
67        mSmallCacheWidth = atoi(property);
68    }
69
70    if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
71        mSmallCacheHeight = atoi(property);
72    }
73
74    if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
75        mLargeCacheWidth = atoi(property);
76    }
77
78    if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
79        mLargeCacheHeight = atoi(property);
80    }
81
82    uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
83    mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
84    mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
85    mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
86    mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
87
88    if (sLogFontRendererCreate) {
89        INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
90                mSmallCacheWidth, mSmallCacheHeight,
91                mLargeCacheWidth, mLargeCacheHeight >> 1,
92                mLargeCacheWidth, mLargeCacheHeight >> 1,
93                mLargeCacheWidth, mLargeCacheHeight);
94    }
95
96    sLogFontRendererCreate = false;
97}
98
99FontRenderer::~FontRenderer() {
100    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
101        delete mCacheTextures[i];
102    }
103    mCacheTextures.clear();
104
105    if (mInitialized) {
106        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
107        Caches::getInstance().unbindIndicesBuffer();
108        glDeleteBuffers(1, &mIndexBufferID);
109
110        delete[] mTextMesh;
111    }
112
113    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
114    while (it.next()) {
115        delete it.value();
116    }
117    mActiveFonts.clear();
118}
119
120void FontRenderer::flushAllAndInvalidate() {
121    if (mCurrentQuadIndex != 0) {
122        issueDrawCommand();
123    }
124
125    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
126    while (it.next()) {
127        it.value()->invalidateTextureCache();
128    }
129
130    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
131        mCacheTextures[i]->init();
132    }
133
134#if DEBUG_FONT_RENDERER
135    uint16_t totalGlyphs = 0;
136    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
137        totalGlyphs += mCacheTextures[i]->getGlyphCount();
138        // Erase caches, just as a debugging facility
139        if (mCacheTextures[i]->getTexture()) {
140            memset(mCacheTextures[i]->getTexture(), 0,
141                    mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
142        }
143    }
144    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
145#endif
146}
147
148void FontRenderer::flushLargeCaches() {
149    // Start from 1; don't deallocate smallest/default texture
150    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
151        CacheTexture* cacheTexture = mCacheTextures[i];
152        if (cacheTexture->getTexture()) {
153            cacheTexture->init();
154            LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
155            while (it.next()) {
156                it.value()->invalidateTextureCache(cacheTexture);
157            }
158            cacheTexture->releaseTexture();
159        }
160    }
161}
162
163CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
164        uint32_t* startX, uint32_t* startY) {
165    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
166        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
167            return mCacheTextures[i];
168        }
169    }
170    // Could not fit glyph into current cache textures
171    return NULL;
172}
173
174void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
175        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
176    checkInit();
177    cachedGlyph->mIsValid = false;
178    // If the glyph is too tall, don't cache it
179    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
180                mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
181        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
182                (int) glyph.fWidth, (int) glyph.fHeight);
183        return;
184    }
185
186    // Now copy the bitmap into the cache texture
187    uint32_t startX = 0;
188    uint32_t startY = 0;
189
190    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
191
192    if (!cacheTexture) {
193        if (!precaching) {
194            // If the new glyph didn't fit and we are not just trying to precache it,
195            // clear out the cache and try again
196            flushAllAndInvalidate();
197            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
198        }
199
200        if (!cacheTexture) {
201            // either the glyph didn't fit or we're precaching and will cache it when we draw
202            return;
203        }
204    }
205
206    cachedGlyph->mCacheTexture = cacheTexture;
207
208    *retOriginX = startX;
209    *retOriginY = startY;
210
211    uint32_t endX = startX + glyph.fWidth;
212    uint32_t endY = startY + glyph.fHeight;
213
214    uint32_t cacheWidth = cacheTexture->getWidth();
215
216    if (!cacheTexture->getTexture()) {
217        Caches::getInstance().activeTexture(0);
218        // Large-glyph texture memory is allocated only as needed
219        cacheTexture->allocateTexture();
220    }
221
222    // Tells us whether the glyphs is B&W (1 bit per pixel)
223    // or anti-aliased (8 bits per pixel)
224    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
225
226    uint8_t* cacheBuffer = cacheTexture->getTexture();
227    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
228
229    // Zero out the borders
230    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
231        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
232        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
233    }
234
235    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
236            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
237        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
238        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
239    }
240
241    // Copy the glyph image, taking the mask format into account
242    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
243    int stride = glyph.rowBytes();
244
245    switch (format) {
246        case SkMask::kA8_Format: {
247            if (mGammaTable) {
248                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
249                    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
250                        uint8_t tempCol = bitmapBuffer[bY + bX];
251                        cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
252                    }
253                }
254            } else {
255                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
256                    memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
257                            glyph.fWidth);
258                }
259            }
260            break;
261        }
262        case SkMask::kBW_Format: {
263            static const uint8_t COLORS[2] = { 0, 255 };
264
265            for (cacheY = startY; cacheY < endY; cacheY++) {
266                cacheX = startX;
267                int rowBytes = stride;
268                uint8_t* buffer = bitmapBuffer;
269
270                while (--rowBytes >= 0) {
271                    uint8_t b = *buffer++;
272                    for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
273                        cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
274                    }
275                }
276
277                bitmapBuffer += stride;
278            }
279            break;
280        }
281        default:
282            ALOGW("Unkown glyph format: 0x%x", format);
283            break;
284    }
285
286    cachedGlyph->mIsValid = true;
287}
288
289CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
290    CacheTexture* cacheTexture = new CacheTexture(width, height);
291
292    if (allocate) {
293        Caches::getInstance().activeTexture(0);
294        cacheTexture->allocateTexture();
295    }
296
297    return cacheTexture;
298}
299
300void FontRenderer::initTextTexture() {
301    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
302        delete mCacheTextures[i];
303    }
304    mCacheTextures.clear();
305
306    mUploadTexture = false;
307    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
308    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
309    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
310    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
311    mCurrentCacheTexture = mCacheTextures[0];
312}
313
314// Avoid having to reallocate memory and render quad by quad
315void FontRenderer::initVertexArrayBuffers() {
316    uint32_t numIndices = mMaxNumberOfQuads * 6;
317    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
318    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
319
320    // Four verts, two triangles , six indices per quad
321    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
322        int i6 = i * 6;
323        int i4 = i * 4;
324
325        indexBufferData[i6 + 0] = i4 + 0;
326        indexBufferData[i6 + 1] = i4 + 1;
327        indexBufferData[i6 + 2] = i4 + 2;
328
329        indexBufferData[i6 + 3] = i4 + 0;
330        indexBufferData[i6 + 4] = i4 + 2;
331        indexBufferData[i6 + 5] = i4 + 3;
332    }
333
334    glGenBuffers(1, &mIndexBufferID);
335    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
336    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
337
338    free(indexBufferData);
339
340    uint32_t coordSize = 2;
341    uint32_t uvSize = 2;
342    uint32_t vertsPerQuad = 4;
343    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
344    mTextMesh = new float[vertexBufferSize];
345}
346
347// We don't want to allocate anything unless we actually draw text
348void FontRenderer::checkInit() {
349    if (mInitialized) {
350        return;
351    }
352
353    initTextTexture();
354    initVertexArrayBuffers();
355
356    mInitialized = true;
357}
358
359void FontRenderer::updateDrawParams() {
360    if (mCurrentQuadIndex != mLastQuadIndex) {
361        mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
362        mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
363        mDrawCacheTextures.add(mCurrentCacheTexture);
364        mLastQuadIndex = mCurrentQuadIndex;
365    }
366}
367
368void FontRenderer::checkTextureUpdate() {
369    if (!mUploadTexture) {
370        return;
371    }
372
373    Caches& caches = Caches::getInstance();
374    GLuint lastTextureId = 0;
375    // Iterate over all the cache textures and see which ones need to be updated
376    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
377        CacheTexture* cacheTexture = mCacheTextures[i];
378        if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
379            // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
380            // of data. So expand the dirty rect to the encompassing horizontal stripe.
381            const Rect* dirtyRect = cacheTexture->getDirtyRect();
382            uint32_t x = 0;
383            uint32_t y = dirtyRect->top;
384            uint32_t width = cacheTexture->getWidth();
385            uint32_t height = dirtyRect->getHeight();
386            void* textureData = cacheTexture->getTexture() + y * width;
387
388            if (cacheTexture->getTextureId() != lastTextureId) {
389                lastTextureId = cacheTexture->getTextureId();
390                caches.activeTexture(0);
391                glBindTexture(GL_TEXTURE_2D, lastTextureId);
392            }
393#if DEBUG_FONT_RENDERER
394            ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
395                    i, x, y, width, height);
396#endif
397            glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
398                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
399            cacheTexture->setDirty(false);
400        }
401    }
402
403    mUploadTexture = false;
404}
405
406void FontRenderer::issueDrawCommand() {
407    updateDrawParams();
408    checkTextureUpdate();
409
410    Caches& caches = Caches::getInstance();
411    caches.bindIndicesBuffer(mIndexBufferID);
412    if (!mDrawn) {
413        float* buffer = mTextMesh;
414        int offset = 2;
415
416        bool force = caches.unbindMeshBuffer();
417        caches.bindPositionVertexPointer(force, buffer);
418        caches.bindTexCoordsVertexPointer(force, buffer + offset);
419    }
420
421    for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
422        uint16_t* offset = mDrawOffsets[i];
423        uint32_t count = mDrawCounts[i];
424        CacheTexture* texture = mDrawCacheTextures[i];
425
426        caches.activeTexture(0);
427        glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
428
429        texture->setLinearFiltering(mLinearFiltering, false);
430
431        glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
432    }
433
434    mDrawn = true;
435
436    mCurrentQuadIndex = 0;
437    mLastQuadIndex = 0;
438    mDrawOffsets.clear();
439    mDrawCounts.clear();
440    mDrawCacheTextures.clear();
441}
442
443void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
444        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
445        float x4, float y4, float u4, float v4, CacheTexture* texture) {
446    if (texture != mCurrentCacheTexture) {
447        updateDrawParams();
448        // Now use the new texture id
449        mCurrentCacheTexture = texture;
450    }
451
452    const uint32_t vertsPerQuad = 4;
453    const uint32_t floatsPerVert = 4;
454    float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
455
456    (*currentPos++) = x1;
457    (*currentPos++) = y1;
458    (*currentPos++) = u1;
459    (*currentPos++) = v1;
460
461    (*currentPos++) = x2;
462    (*currentPos++) = y2;
463    (*currentPos++) = u2;
464    (*currentPos++) = v2;
465
466    (*currentPos++) = x3;
467    (*currentPos++) = y3;
468    (*currentPos++) = u3;
469    (*currentPos++) = v3;
470
471    (*currentPos++) = x4;
472    (*currentPos++) = y4;
473    (*currentPos++) = u4;
474    (*currentPos++) = v4;
475
476    mCurrentQuadIndex++;
477}
478
479void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
480        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
481        float x4, float y4, float u4, float v4, CacheTexture* texture) {
482
483    if (mClip &&
484            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
485        return;
486    }
487
488    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
489
490    if (mBounds) {
491        mBounds->left = fmin(mBounds->left, x1);
492        mBounds->top = fmin(mBounds->top, y3);
493        mBounds->right = fmax(mBounds->right, x3);
494        mBounds->bottom = fmax(mBounds->bottom, y1);
495    }
496
497    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
498        issueDrawCommand();
499    }
500}
501
502void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
503        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
504        float x4, float y4, float u4, float v4, CacheTexture* texture) {
505
506    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
507
508    if (mBounds) {
509        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
510        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
511        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
512        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
513    }
514
515    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
516        issueDrawCommand();
517    }
518}
519
520void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
521    mCurrentFont = Font::create(this, paint, matrix);
522}
523
524FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
525        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
526    checkInit();
527
528    if (!mCurrentFont) {
529        DropShadow image;
530        image.width = 0;
531        image.height = 0;
532        image.image = NULL;
533        image.penX = 0;
534        image.penY = 0;
535        return image;
536    }
537
538    mDrawn = false;
539    mClip = NULL;
540    mBounds = NULL;
541
542    Rect bounds;
543    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
544
545    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
546    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
547    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
548
549    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
550        dataBuffer[i] = 0;
551    }
552
553    int penX = radius - bounds.left;
554    int penY = radius - bounds.bottom;
555
556    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
557            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
558    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
559
560    DropShadow image;
561    image.width = paddedWidth;
562    image.height = paddedHeight;
563    image.image = dataBuffer;
564    image.penX = penX;
565    image.penY = penY;
566
567    return image;
568}
569
570void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
571    checkInit();
572
573    mDrawn = false;
574    mBounds = bounds;
575    mClip = clip;
576}
577
578void FontRenderer::finishRender() {
579    mBounds = NULL;
580    mClip = NULL;
581
582    if (mCurrentQuadIndex != 0) {
583        issueDrawCommand();
584    }
585}
586
587void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
588    Font* font = Font::create(this, paint, matrix);
589    font->precache(paint, text, numGlyphs);
590}
591
592bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
593        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
594        const float* positions, Rect* bounds) {
595    if (!mCurrentFont) {
596        ALOGE("No font set");
597        return false;
598    }
599
600    initRender(clip, bounds);
601    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
602    finishRender();
603
604    return mDrawn;
605}
606
607bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
608        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
609        float hOffset, float vOffset, Rect* bounds) {
610    if (!mCurrentFont) {
611        ALOGE("No font set");
612        return false;
613    }
614
615    initRender(clip, bounds);
616    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
617    finishRender();
618
619    return mDrawn;
620}
621
622void FontRenderer::removeFont(const Font* font) {
623    mActiveFonts.remove(font->getDescription());
624
625    if (mCurrentFont == font) {
626        mCurrentFont = NULL;
627    }
628}
629
630void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
631    // Compute gaussian weights for the blur
632    // e is the euler's number
633    float e = 2.718281828459045f;
634    float pi = 3.1415926535897932f;
635    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
636    // x is of the form [-radius .. 0 .. radius]
637    // and sigma varies with radius.
638    // Based on some experimental radius values and sigma's
639    // we approximately fit sigma = f(radius) as
640    // sigma = radius * 0.3  + 0.6
641    // The larger the radius gets, the more our gaussian blur
642    // will resemble a box blur since with large sigma
643    // the gaussian curve begins to lose its shape
644    float sigma = 0.3f * (float) radius + 0.6f;
645
646    // Now compute the coefficints
647    // We will store some redundant values to save some math during
648    // the blur calculations
649    // precompute some values
650    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
651    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
652
653    float normalizeFactor = 0.0f;
654    for (int32_t r = -radius; r <= radius; r ++) {
655        float floatR = (float) r;
656        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
657        normalizeFactor += weights[r + radius];
658    }
659
660    //Now we need to normalize the weights because all our coefficients need to add up to one
661    normalizeFactor = 1.0f / normalizeFactor;
662    for (int32_t r = -radius; r <= radius; r ++) {
663        weights[r + radius] *= normalizeFactor;
664    }
665}
666
667void FontRenderer::horizontalBlur(float* weights, int32_t radius,
668        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
669    float blurredPixel = 0.0f;
670    float currentPixel = 0.0f;
671
672    for (int32_t y = 0; y < height; y ++) {
673
674        const uint8_t* input = source + y * width;
675        uint8_t* output = dest + y * width;
676
677        for (int32_t x = 0; x < width; x ++) {
678            blurredPixel = 0.0f;
679            const float* gPtr = weights;
680            // Optimization for non-border pixels
681            if (x > radius && x < (width - radius)) {
682                const uint8_t *i = input + (x - radius);
683                for (int r = -radius; r <= radius; r ++) {
684                    currentPixel = (float) (*i);
685                    blurredPixel += currentPixel * gPtr[0];
686                    gPtr++;
687                    i++;
688                }
689            } else {
690                for (int32_t r = -radius; r <= radius; r ++) {
691                    // Stepping left and right away from the pixel
692                    int validW = x + r;
693                    if (validW < 0) {
694                        validW = 0;
695                    }
696                    if (validW > width - 1) {
697                        validW = width - 1;
698                    }
699
700                    currentPixel = (float) input[validW];
701                    blurredPixel += currentPixel * gPtr[0];
702                    gPtr++;
703                }
704            }
705            *output = (uint8_t)blurredPixel;
706            output ++;
707        }
708    }
709}
710
711void FontRenderer::verticalBlur(float* weights, int32_t radius,
712        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
713    float blurredPixel = 0.0f;
714    float currentPixel = 0.0f;
715
716    for (int32_t y = 0; y < height; y ++) {
717        uint8_t* output = dest + y * width;
718
719        for (int32_t x = 0; x < width; x ++) {
720            blurredPixel = 0.0f;
721            const float* gPtr = weights;
722            const uint8_t* input = source + x;
723            // Optimization for non-border pixels
724            if (y > radius && y < (height - radius)) {
725                const uint8_t *i = input + ((y - radius) * width);
726                for (int32_t r = -radius; r <= radius; r ++) {
727                    currentPixel = (float)(*i);
728                    blurredPixel += currentPixel * gPtr[0];
729                    gPtr++;
730                    i += width;
731                }
732            } else {
733                for (int32_t r = -radius; r <= radius; r ++) {
734                    int validH = y + r;
735                    // Clamp to zero and width
736                    if (validH < 0) {
737                        validH = 0;
738                    }
739                    if (validH > height - 1) {
740                        validH = height - 1;
741                    }
742
743                    const uint8_t *i = input + validH * width;
744                    currentPixel = (float) (*i);
745                    blurredPixel += currentPixel * gPtr[0];
746                    gPtr++;
747                }
748            }
749            *output = (uint8_t) blurredPixel;
750            output++;
751        }
752    }
753}
754
755
756void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
757    float *gaussian = new float[2 * radius + 1];
758    computeGaussianWeights(gaussian, radius);
759
760    uint8_t* scratch = new uint8_t[width * height];
761
762    horizontalBlur(gaussian, radius, image, scratch, width, height);
763    verticalBlur(gaussian, radius, scratch, image, width, height);
764
765    delete[] gaussian;
766    delete[] scratch;
767}
768
769}; // namespace uirenderer
770}; // namespace android
771