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, buffer);
378        caches.bindTexCoordsVertexPointer(force, buffer + offset);
379    }
380
381    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
382
383    mDrawn = true;
384}
385
386void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
387        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
388        float x4, float y4, float u4, float v4, CacheTexture* texture) {
389    if (texture != mCurrentCacheTexture) {
390        if (mCurrentQuadIndex != 0) {
391            // First, draw everything stored already which uses the previous texture
392            issueDrawCommand();
393            mCurrentQuadIndex = 0;
394        }
395        // Now use the new texture id
396        mCurrentCacheTexture = texture;
397    }
398
399    const uint32_t vertsPerQuad = 4;
400    const uint32_t floatsPerVert = 4;
401    float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
402
403    (*currentPos++) = x1;
404    (*currentPos++) = y1;
405    (*currentPos++) = u1;
406    (*currentPos++) = v1;
407
408    (*currentPos++) = x2;
409    (*currentPos++) = y2;
410    (*currentPos++) = u2;
411    (*currentPos++) = v2;
412
413    (*currentPos++) = x3;
414    (*currentPos++) = y3;
415    (*currentPos++) = u3;
416    (*currentPos++) = v3;
417
418    (*currentPos++) = x4;
419    (*currentPos++) = y4;
420    (*currentPos++) = u4;
421    (*currentPos++) = v4;
422
423    mCurrentQuadIndex++;
424}
425
426void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
427        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
428        float x4, float y4, float u4, float v4, CacheTexture* texture) {
429
430    if (mClip &&
431            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
432        return;
433    }
434
435    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
436
437    if (mBounds) {
438        mBounds->left = fmin(mBounds->left, x1);
439        mBounds->top = fmin(mBounds->top, y3);
440        mBounds->right = fmax(mBounds->right, x3);
441        mBounds->bottom = fmax(mBounds->bottom, y1);
442    }
443
444    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
445        issueDrawCommand();
446        mCurrentQuadIndex = 0;
447    }
448}
449
450void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
451        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
452        float x4, float y4, float u4, float v4, CacheTexture* texture) {
453
454    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
455
456    if (mBounds) {
457        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
458        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
459        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
460        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
461    }
462
463    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
464        issueDrawCommand();
465        mCurrentQuadIndex = 0;
466    }
467}
468
469void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
470    int flags = 0;
471    if (paint->isFakeBoldText()) {
472        flags |= Font::kFakeBold;
473    }
474
475    const float skewX = paint->getTextSkewX();
476    uint32_t italicStyle = *(uint32_t*) &skewX;
477    const float scaleXFloat = paint->getTextScaleX();
478    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
479    SkPaint::Style style = paint->getStyle();
480    const float strokeWidthFloat = paint->getStrokeWidth();
481    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
482    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
483            scaleX, style, strokeWidth);
484
485}
486
487FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
488        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
489    checkInit();
490
491    if (!mCurrentFont) {
492        DropShadow image;
493        image.width = 0;
494        image.height = 0;
495        image.image = NULL;
496        image.penX = 0;
497        image.penY = 0;
498        return image;
499    }
500
501    mDrawn = false;
502    mClip = NULL;
503    mBounds = NULL;
504
505    Rect bounds;
506    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
507
508    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
509    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
510    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
511
512    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
513        dataBuffer[i] = 0;
514    }
515
516    int penX = radius - bounds.left;
517    int penY = radius - bounds.bottom;
518
519    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
520            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
521    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
522
523    DropShadow image;
524    image.width = paddedWidth;
525    image.height = paddedHeight;
526    image.image = dataBuffer;
527    image.penX = penX;
528    image.penY = penY;
529
530    return image;
531}
532
533void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
534    checkInit();
535
536    mDrawn = false;
537    mBounds = bounds;
538    mClip = clip;
539}
540
541void FontRenderer::finishRender() {
542    mBounds = NULL;
543    mClip = NULL;
544
545    if (mCurrentQuadIndex != 0) {
546        issueDrawCommand();
547        mCurrentQuadIndex = 0;
548    }
549}
550
551void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
552    int flags = 0;
553    if (paint->isFakeBoldText()) {
554        flags |= Font::kFakeBold;
555    }
556    const float skewX = paint->getTextSkewX();
557    uint32_t italicStyle = *(uint32_t*) &skewX;
558    const float scaleXFloat = paint->getTextScaleX();
559    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
560    SkPaint::Style style = paint->getStyle();
561    const float strokeWidthFloat = paint->getStrokeWidth();
562    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
563    float fontSize = paint->getTextSize();
564    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
565            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
566
567    font->precache(paint, text, numGlyphs);
568}
569
570bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
571        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
572    if (!mCurrentFont) {
573        ALOGE("No font set");
574        return false;
575    }
576
577    initRender(clip, bounds);
578    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
579    finishRender();
580
581    return mDrawn;
582}
583
584bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
585        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
586        const float* positions, Rect* bounds) {
587    if (!mCurrentFont) {
588        ALOGE("No font set");
589        return false;
590    }
591
592    initRender(clip, bounds);
593    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
594    finishRender();
595
596    return mDrawn;
597}
598
599bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
600        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
601        float hOffset, float vOffset, Rect* bounds) {
602    if (!mCurrentFont) {
603        ALOGE("No font set");
604        return false;
605    }
606
607    initRender(clip, bounds);
608    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
609    finishRender();
610
611    return mDrawn;
612}
613
614void FontRenderer::removeFont(const Font* font) {
615    for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
616        if (mActiveFonts[ct] == font) {
617            mActiveFonts.removeAt(ct);
618            break;
619        }
620    }
621
622    if (mCurrentFont == font) {
623        mCurrentFont = NULL;
624    }
625}
626
627void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
628    // Compute gaussian weights for the blur
629    // e is the euler's number
630    float e = 2.718281828459045f;
631    float pi = 3.1415926535897932f;
632    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
633    // x is of the form [-radius .. 0 .. radius]
634    // and sigma varies with radius.
635    // Based on some experimental radius values and sigma's
636    // we approximately fit sigma = f(radius) as
637    // sigma = radius * 0.3  + 0.6
638    // The larger the radius gets, the more our gaussian blur
639    // will resemble a box blur since with large sigma
640    // the gaussian curve begins to lose its shape
641    float sigma = 0.3f * (float) radius + 0.6f;
642
643    // Now compute the coefficints
644    // We will store some redundant values to save some math during
645    // the blur calculations
646    // precompute some values
647    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
648    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
649
650    float normalizeFactor = 0.0f;
651    for (int32_t r = -radius; r <= radius; r ++) {
652        float floatR = (float) r;
653        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
654        normalizeFactor += weights[r + radius];
655    }
656
657    //Now we need to normalize the weights because all our coefficients need to add up to one
658    normalizeFactor = 1.0f / normalizeFactor;
659    for (int32_t r = -radius; r <= radius; r ++) {
660        weights[r + radius] *= normalizeFactor;
661    }
662}
663
664void FontRenderer::horizontalBlur(float* weights, int32_t radius,
665        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
666    float blurredPixel = 0.0f;
667    float currentPixel = 0.0f;
668
669    for (int32_t y = 0; y < height; y ++) {
670
671        const uint8_t* input = source + y * width;
672        uint8_t* output = dest + y * width;
673
674        for (int32_t x = 0; x < width; x ++) {
675            blurredPixel = 0.0f;
676            const float* gPtr = weights;
677            // Optimization for non-border pixels
678            if (x > radius && x < (width - radius)) {
679                const uint8_t *i = input + (x - radius);
680                for (int r = -radius; r <= radius; r ++) {
681                    currentPixel = (float) (*i);
682                    blurredPixel += currentPixel * gPtr[0];
683                    gPtr++;
684                    i++;
685                }
686            } else {
687                for (int32_t r = -radius; r <= radius; r ++) {
688                    // Stepping left and right away from the pixel
689                    int validW = x + r;
690                    if (validW < 0) {
691                        validW = 0;
692                    }
693                    if (validW > width - 1) {
694                        validW = width - 1;
695                    }
696
697                    currentPixel = (float) input[validW];
698                    blurredPixel += currentPixel * gPtr[0];
699                    gPtr++;
700                }
701            }
702            *output = (uint8_t)blurredPixel;
703            output ++;
704        }
705    }
706}
707
708void FontRenderer::verticalBlur(float* weights, int32_t radius,
709        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
710    float blurredPixel = 0.0f;
711    float currentPixel = 0.0f;
712
713    for (int32_t y = 0; y < height; y ++) {
714        uint8_t* output = dest + y * width;
715
716        for (int32_t x = 0; x < width; x ++) {
717            blurredPixel = 0.0f;
718            const float* gPtr = weights;
719            const uint8_t* input = source + x;
720            // Optimization for non-border pixels
721            if (y > radius && y < (height - radius)) {
722                const uint8_t *i = input + ((y - radius) * width);
723                for (int32_t r = -radius; r <= radius; r ++) {
724                    currentPixel = (float)(*i);
725                    blurredPixel += currentPixel * gPtr[0];
726                    gPtr++;
727                    i += width;
728                }
729            } else {
730                for (int32_t r = -radius; r <= radius; r ++) {
731                    int validH = y + r;
732                    // Clamp to zero and width
733                    if (validH < 0) {
734                        validH = 0;
735                    }
736                    if (validH > height - 1) {
737                        validH = height - 1;
738                    }
739
740                    const uint8_t *i = input + validH * width;
741                    currentPixel = (float) (*i);
742                    blurredPixel += currentPixel * gPtr[0];
743                    gPtr++;
744                }
745            }
746            *output = (uint8_t) blurredPixel;
747            output++;
748        }
749    }
750}
751
752
753void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
754    float *gaussian = new float[2 * radius + 1];
755    computeGaussianWeights(gaussian, radius);
756
757    uint8_t* scratch = new uint8_t[width * height];
758
759    horizontalBlur(gaussian, radius, image, scratch, width, height);
760    verticalBlur(gaussian, radius, scratch, image, width, height);
761
762    delete[] gaussian;
763    delete[] scratch;
764}
765
766}; // namespace uirenderer
767}; // namespace android
768