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