FontRenderer.cpp revision b6039811fa0fbc23f25c47491810faeb04ce3125
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 "Caches.h"
29
30namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
34// Defines
35///////////////////////////////////////////////////////////////////////////////
36
37#define DEFAULT_TEXT_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_CACHE_HEIGHT 256
39#define MAX_TEXT_CACHE_WIDTH 2048
40#define TEXTURE_BORDER_SIZE 1
41
42#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
43
44///////////////////////////////////////////////////////////////////////////////
45// CacheTextureLine
46///////////////////////////////////////////////////////////////////////////////
47
48bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
49    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
50        return false;
51    }
52
53    if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE * 2 < mMaxWidth) {
54        *retOriginX = mCurrentCol + TEXTURE_BORDER_SIZE;
55        *retOriginY = mCurrentRow + TEXTURE_BORDER_SIZE;
56        mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE * 2;
57        mDirty = true;
58        return true;
59    }
60
61    return false;
62}
63
64///////////////////////////////////////////////////////////////////////////////
65// Font
66///////////////////////////////////////////////////////////////////////////////
67
68Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
69        int flags, uint32_t italicStyle, uint32_t scaleX,
70        SkPaint::Style style, uint32_t strokeWidth) :
71        mState(state), mFontId(fontId), mFontSize(fontSize),
72        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
73        mStyle(style), mStrokeWidth(mStrokeWidth) {
74}
75
76
77Font::~Font() {
78    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
79        if (mState->mActiveFonts[ct] == this) {
80            mState->mActiveFonts.removeAt(ct);
81            break;
82        }
83    }
84
85    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
86        delete mCachedGlyphs.valueAt(i);
87    }
88}
89
90void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
91    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
92        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
93        if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
94            cachedGlyph->mIsValid = false;
95        }
96    }
97}
98
99void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
100        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
101    int nPenX = x + glyph->mBitmapLeft;
102    int nPenY = y + glyph->mBitmapTop;
103
104    int width = (int) glyph->mBitmapWidth;
105    int height = (int) glyph->mBitmapHeight;
106
107    if (bounds->bottom > nPenY) {
108        bounds->bottom = nPenY;
109    }
110    if (bounds->left > nPenX) {
111        bounds->left = nPenX;
112    }
113    if (bounds->right < nPenX + width) {
114        bounds->right = nPenX + width;
115    }
116    if (bounds->top < nPenY + height) {
117        bounds->top = nPenY + height;
118    }
119}
120
121void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
122        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
123    int nPenX = x + glyph->mBitmapLeft;
124    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
125
126    float u1 = glyph->mBitmapMinU;
127    float u2 = glyph->mBitmapMaxU;
128    float v1 = glyph->mBitmapMinV;
129    float v2 = glyph->mBitmapMaxV;
130
131    int width = (int) glyph->mBitmapWidth;
132    int height = (int) glyph->mBitmapHeight;
133
134    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
135            nPenX + width, nPenY, u2, v2,
136            nPenX + width, nPenY - height, u2, v1,
137            nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
138}
139
140void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
141        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
142    int nPenX = x + glyph->mBitmapLeft;
143    int nPenY = y + glyph->mBitmapTop;
144
145    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
146    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
147
148    CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
149    uint32_t cacheWidth = cacheTexture->mWidth;
150    const uint8_t* cacheBuffer = cacheTexture->mTexture;
151
152    uint32_t cacheX = 0, cacheY = 0;
153    int32_t bX = 0, bY = 0;
154    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
155        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
156#if DEBUG_FONT_RENDERER
157            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
158                ALOGE("Skipping invalid index");
159                continue;
160            }
161#endif
162            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
163            bitmap[bY * bitmapW + bX] = tempCol;
164        }
165    }
166}
167
168void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
169        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
170    const float halfWidth = glyph->mBitmapWidth * 0.5f;
171    const float height = glyph->mBitmapHeight;
172
173    vOffset += glyph->mBitmapTop + height;
174
175    SkPoint destination[4];
176    measure.getPosTan(x + hOffset +  glyph->mBitmapLeft + halfWidth, position, tangent);
177
178    // Move along the tangent and offset by the normal
179    destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
180            -tangent->fY * halfWidth + tangent->fX * vOffset);
181    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
182            tangent->fY * halfWidth + tangent->fX * vOffset);
183    destination[2].set(destination[1].fX + tangent->fY * height,
184            destination[1].fY - tangent->fX * height);
185    destination[3].set(destination[0].fX + tangent->fY * height,
186            destination[0].fY - tangent->fX * height);
187
188    const float u1 = glyph->mBitmapMinU;
189    const float u2 = glyph->mBitmapMaxU;
190    const float v1 = glyph->mBitmapMinV;
191    const float v2 = glyph->mBitmapMaxV;
192
193    mState->appendRotatedMeshQuad(
194            position->fX + destination[0].fX,
195            position->fY + destination[0].fY, u1, v2,
196            position->fX + destination[1].fX,
197            position->fY + destination[1].fY, u2, v2,
198            position->fX + destination[2].fX,
199            position->fY + destination[2].fY, u2, v1,
200            position->fX + destination[3].fX,
201            position->fY + destination[3].fY, u1, v1,
202            glyph->mCachedTextureLine->mCacheTexture);
203}
204
205CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
206    CachedGlyphInfo* cachedGlyph = NULL;
207    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
208    if (index >= 0) {
209        cachedGlyph = mCachedGlyphs.valueAt(index);
210    } else {
211        cachedGlyph = cacheGlyph(paint, textUnit);
212    }
213
214    // Is the glyph still in texture cache?
215    if (!cachedGlyph->mIsValid) {
216        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
217        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
218    }
219
220    return cachedGlyph;
221}
222
223void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
224        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
225    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
226        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
227                bitmapW, bitmapH, NULL, NULL);
228    } else {
229        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
230                0, 0, NULL, NULL);
231    }
232}
233
234void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
235            int numGlyphs, int x, int y, const float* positions) {
236    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
237            0, 0, NULL, positions);
238}
239
240void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
241        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
242    if (numGlyphs == 0 || text == NULL || len == 0) {
243        return;
244    }
245
246    text += start;
247
248    int glyphsCount = 0;
249    SkFixed prevRsbDelta = 0;
250
251    float penX = 0.0f;
252
253    SkPoint position;
254    SkVector tangent;
255
256    SkPathMeasure measure(*path, false);
257    float pathLength = SkScalarToFloat(measure.getLength());
258
259    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
260        float textWidth = SkScalarToFloat(paint->measureText(text, len));
261        float pathOffset = pathLength;
262        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
263            textWidth *= 0.5f;
264            pathOffset *= 0.5f;
265        }
266        penX += pathOffset - textWidth;
267    }
268
269    while (glyphsCount < numGlyphs && penX < pathLength) {
270        glyph_t glyph = GET_GLYPH(text);
271
272        if (IS_END_OF_STRING(glyph)) {
273            break;
274        }
275
276        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
277        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
278        prevRsbDelta = cachedGlyph->mRsbDelta;
279
280        if (cachedGlyph->mIsValid) {
281            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
282        }
283
284        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
285
286        glyphsCount++;
287    }
288}
289
290void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
291        int numGlyphs, Rect *bounds, const float* positions) {
292    if (bounds == NULL) {
293        ALOGE("No return rectangle provided to measure text");
294        return;
295    }
296    bounds->set(1e6, -1e6, -1e6, 1e6);
297    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
298}
299
300void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
301        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
302        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
303    if (numGlyphs == 0 || text == NULL || len == 0) {
304        return;
305    }
306
307    static RenderGlyph gRenderGlyph[] = {
308            &android::uirenderer::Font::drawCachedGlyph,
309            &android::uirenderer::Font::drawCachedGlyphBitmap,
310            &android::uirenderer::Font::measureCachedGlyph
311    };
312    RenderGlyph render = gRenderGlyph[mode];
313
314    text += start;
315    int glyphsCount = 0;
316
317    if (CC_LIKELY(positions == NULL)) {
318        SkFixed prevRsbDelta = 0;
319
320        float penX = x + 0.5f;
321        int penY = y;
322
323        while (glyphsCount < numGlyphs) {
324            glyph_t glyph = GET_GLYPH(text);
325
326            // Reached the end of the string
327            if (IS_END_OF_STRING(glyph)) {
328                break;
329            }
330
331            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
332            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
333            prevRsbDelta = cachedGlyph->mRsbDelta;
334
335            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
336            if (cachedGlyph->mIsValid) {
337                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
338                        bitmap, bitmapW, bitmapH, bounds, positions);
339            }
340
341            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
342
343            glyphsCount++;
344        }
345    } else {
346        const SkPaint::Align align = paint->getTextAlign();
347
348        // This is for renderPosText()
349        while (glyphsCount < numGlyphs) {
350            glyph_t glyph = GET_GLYPH(text);
351
352            // Reached the end of the string
353            if (IS_END_OF_STRING(glyph)) {
354                break;
355            }
356
357            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
358
359            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
360            if (cachedGlyph->mIsValid) {
361                int penX = x + positions[(glyphsCount << 1)];
362                int penY = y + positions[(glyphsCount << 1) + 1];
363
364                switch (align) {
365                    case SkPaint::kRight_Align:
366                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
367                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
368                        break;
369                    case SkPaint::kCenter_Align:
370                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
371                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
372                    default:
373                        break;
374                }
375
376                (*this.*render)(cachedGlyph, penX, penY,
377                        bitmap, bitmapW, bitmapH, bounds, positions);
378            }
379
380            glyphsCount++;
381        }
382    }
383}
384
385void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
386    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
387    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
388    glyph->mBitmapLeft = skiaGlyph.fLeft;
389    glyph->mBitmapTop = skiaGlyph.fTop;
390    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
391    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
392
393    uint32_t startX = 0;
394    uint32_t startY = 0;
395
396    // Get the bitmap for the glyph
397    paint->findImage(skiaGlyph);
398    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
399
400    if (!glyph->mIsValid) {
401        return;
402    }
403
404    uint32_t endX = startX + skiaGlyph.fWidth;
405    uint32_t endY = startY + skiaGlyph.fHeight;
406
407    glyph->mStartX = startX;
408    glyph->mStartY = startY;
409    glyph->mBitmapWidth = skiaGlyph.fWidth;
410    glyph->mBitmapHeight = skiaGlyph.fHeight;
411
412    uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
413    uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
414
415    glyph->mBitmapMinU = startX / (float) cacheWidth;
416    glyph->mBitmapMinV = startY / (float) cacheHeight;
417    glyph->mBitmapMaxU = endX / (float) cacheWidth;
418    glyph->mBitmapMaxV = endY / (float) cacheHeight;
419
420    mState->mUploadTexture = true;
421}
422
423CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
424    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
425    mCachedGlyphs.add(glyph, newGlyph);
426
427    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
428    newGlyph->mGlyphIndex = skiaGlyph.fID;
429    newGlyph->mIsValid = false;
430
431    updateGlyphCache(paint, skiaGlyph, newGlyph);
432
433    return newGlyph;
434}
435
436Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
437        int flags, uint32_t italicStyle, uint32_t scaleX,
438        SkPaint::Style style, uint32_t strokeWidth) {
439    Vector<Font*> &activeFonts = state->mActiveFonts;
440
441    for (uint32_t i = 0; i < activeFonts.size(); i++) {
442        Font* font = activeFonts[i];
443        if (font->mFontId == fontId && font->mFontSize == fontSize &&
444                font->mFlags == flags && font->mItalicStyle == italicStyle &&
445                font->mScaleX == scaleX && font->mStyle == style &&
446                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
447            return font;
448        }
449    }
450
451    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
452            scaleX, style, strokeWidth);
453    activeFonts.push(newFont);
454    return newFont;
455}
456
457///////////////////////////////////////////////////////////////////////////////
458// FontRenderer
459///////////////////////////////////////////////////////////////////////////////
460
461static bool sLogFontRendererCreate = true;
462
463FontRenderer::FontRenderer() {
464    if (sLogFontRendererCreate) {
465        INIT_LOGD("Creating FontRenderer");
466    }
467
468    mGammaTable = NULL;
469    mInitialized = false;
470    mMaxNumberOfQuads = 1024;
471    mCurrentQuadIndex = 0;
472
473    mTextMeshPtr = NULL;
474    mCurrentCacheTexture = NULL;
475    mLastCacheTexture = NULL;
476    mCacheTextureSmall = NULL;
477    mCacheTexture128 = NULL;
478    mCacheTexture256 = NULL;
479    mCacheTexture512 = NULL;
480
481    mLinearFiltering = false;
482
483    mIndexBufferID = 0;
484
485    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
486    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
487
488    char property[PROPERTY_VALUE_MAX];
489    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
490        if (sLogFontRendererCreate) {
491            INIT_LOGD("  Setting text cache width to %s pixels", property);
492        }
493        mSmallCacheWidth = atoi(property);
494    } else {
495        if (sLogFontRendererCreate) {
496            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
497        }
498    }
499
500    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
501        if (sLogFontRendererCreate) {
502            INIT_LOGD("  Setting text cache width to %s pixels", property);
503        }
504        mSmallCacheHeight = atoi(property);
505    } else {
506        if (sLogFontRendererCreate) {
507            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
508        }
509    }
510
511    sLogFontRendererCreate = false;
512}
513
514FontRenderer::~FontRenderer() {
515    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
516        delete mCacheLines[i];
517    }
518    mCacheLines.clear();
519
520    if (mInitialized) {
521        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
522        Caches::getInstance().unbindIndicesBuffer();
523        glDeleteBuffers(1, &mIndexBufferID);
524
525        delete[] mTextMeshPtr;
526        delete mCacheTextureSmall;
527        delete mCacheTexture128;
528        delete mCacheTexture256;
529        delete mCacheTexture512;
530    }
531
532    Vector<Font*> fontsToDereference = mActiveFonts;
533    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
534        delete fontsToDereference[i];
535    }
536}
537
538void FontRenderer::flushAllAndInvalidate() {
539    if (mCurrentQuadIndex != 0) {
540        issueDrawCommand();
541        mCurrentQuadIndex = 0;
542    }
543
544    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
545        mActiveFonts[i]->invalidateTextureCache();
546    }
547
548    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
549        mCacheLines[i]->mCurrentCol = 0;
550    }
551}
552
553void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
554    if (cacheTexture && cacheTexture->mTexture) {
555        glDeleteTextures(1, &cacheTexture->mTextureId);
556        delete[] cacheTexture->mTexture;
557        cacheTexture->mTexture = NULL;
558        cacheTexture->mTextureId = 0;
559    }
560}
561
562void FontRenderer::flushLargeCaches() {
563    if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
564            (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
565            (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
566        // Typical case; no large glyph caches allocated
567        return;
568    }
569
570    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
571        CacheTextureLine* cacheLine = mCacheLines[i];
572        if ((cacheLine->mCacheTexture == mCacheTexture128 ||
573                cacheLine->mCacheTexture == mCacheTexture256 ||
574                cacheLine->mCacheTexture == mCacheTexture512) &&
575                cacheLine->mCacheTexture->mTexture != NULL) {
576            cacheLine->mCurrentCol = 0;
577            for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
578                mActiveFonts[i]->invalidateTextureCache(cacheLine);
579            }
580        }
581    }
582
583    deallocateTextureMemory(mCacheTexture128);
584    deallocateTextureMemory(mCacheTexture256);
585    deallocateTextureMemory(mCacheTexture512);
586}
587
588void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
589    int width = cacheTexture->mWidth;
590    int height = cacheTexture->mHeight;
591
592    cacheTexture->mTexture = new uint8_t[width * height];
593
594    if (!cacheTexture->mTextureId) {
595        glGenTextures(1, &cacheTexture->mTextureId);
596    }
597
598    Caches::getInstance().activeTexture(0);
599    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
600    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
601    // Initialize texture dimensions
602    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
603            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
604
605    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
606    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
607    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
608
609    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
610    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
611}
612
613void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
614        uint32_t* retOriginX, uint32_t* retOriginY) {
615    cachedGlyph->mIsValid = false;
616    // If the glyph is too tall, don't cache it
617    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
618        ALOGE("Font size to large to fit in cache. width, height = %i, %i",
619                (int) glyph.fWidth, (int) glyph.fHeight);
620        return;
621    }
622
623    // Now copy the bitmap into the cache texture
624    uint32_t startX = 0;
625    uint32_t startY = 0;
626
627    bool bitmapFit = false;
628    CacheTextureLine *cacheLine;
629    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
630        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
631        if (bitmapFit) {
632            cacheLine = mCacheLines[i];
633            break;
634        }
635    }
636
637    // If the new glyph didn't fit, flush the state so far and invalidate everything
638    if (!bitmapFit) {
639        flushAllAndInvalidate();
640
641        // Try to fit it again
642        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
643            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
644            if (bitmapFit) {
645                cacheLine = mCacheLines[i];
646                break;
647            }
648        }
649
650        // if we still don't fit, something is wrong and we shouldn't draw
651        if (!bitmapFit) {
652            return;
653        }
654    }
655
656    cachedGlyph->mCachedTextureLine = cacheLine;
657
658    *retOriginX = startX;
659    *retOriginY = startY;
660
661    uint32_t endX = startX + glyph.fWidth;
662    uint32_t endY = startY + glyph.fHeight;
663
664    uint32_t cacheWidth = cacheLine->mMaxWidth;
665
666    CacheTexture* cacheTexture = cacheLine->mCacheTexture;
667    if (!cacheTexture->mTexture) {
668        // Large-glyph texture memory is allocated only as needed
669        allocateTextureMemory(cacheTexture);
670    }
671
672    uint8_t* cacheBuffer = cacheTexture->mTexture;
673    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
674    unsigned int stride = glyph.rowBytes();
675
676    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
677
678    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
679        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
680        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
681    }
682
683    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
684            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
685        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
686        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
687    }
688
689    if (mGammaTable) {
690        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
691            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
692                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
693                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
694            }
695        }
696    } else {
697        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
698            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
699                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
700                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
701            }
702        }
703    }
704
705    cachedGlyph->mIsValid = true;
706}
707
708CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
709    CacheTexture* cacheTexture = new CacheTexture(width, height);
710
711    if (allocate) {
712        allocateTextureMemory(cacheTexture);
713    }
714
715    return cacheTexture;
716}
717
718void FontRenderer::initTextTexture() {
719    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
720        delete mCacheLines[i];
721    }
722    mCacheLines.clear();
723
724    if (mCacheTextureSmall) {
725        delete mCacheTextureSmall;
726        delete mCacheTexture128;
727        delete mCacheTexture256;
728        delete mCacheTexture512;
729    }
730
731    // Next, use other, separate caches for large glyphs.
732    uint16_t maxWidth = 0;
733    if (Caches::hasInstance()) {
734        maxWidth = Caches::getInstance().maxTextureSize;
735    }
736
737    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
738        maxWidth = MAX_TEXT_CACHE_WIDTH;
739    }
740
741    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
742    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
743    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
744    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
745    mCurrentCacheTexture = mCacheTextureSmall;
746
747    mUploadTexture = false;
748    // Split up our default cache texture into lines of certain widths
749    int nextLine = 0;
750    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
751    nextLine += mCacheLines.top()->mMaxHeight;
752    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
753    nextLine += mCacheLines.top()->mMaxHeight;
754    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
755    nextLine += mCacheLines.top()->mMaxHeight;
756    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
757    nextLine += mCacheLines.top()->mMaxHeight;
758    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
759    nextLine += mCacheLines.top()->mMaxHeight;
760    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
761    nextLine += mCacheLines.top()->mMaxHeight;
762    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
763            nextLine, 0, mCacheTextureSmall));
764
765    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
766    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
767    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
768    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
769    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
770}
771
772// Avoid having to reallocate memory and render quad by quad
773void FontRenderer::initVertexArrayBuffers() {
774    uint32_t numIndices = mMaxNumberOfQuads * 6;
775    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
776    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
777
778    // Four verts, two triangles , six indices per quad
779    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
780        int i6 = i * 6;
781        int i4 = i * 4;
782
783        indexBufferData[i6 + 0] = i4 + 0;
784        indexBufferData[i6 + 1] = i4 + 1;
785        indexBufferData[i6 + 2] = i4 + 2;
786
787        indexBufferData[i6 + 3] = i4 + 0;
788        indexBufferData[i6 + 4] = i4 + 2;
789        indexBufferData[i6 + 5] = i4 + 3;
790    }
791
792    glGenBuffers(1, &mIndexBufferID);
793    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
794    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
795
796    free(indexBufferData);
797
798    uint32_t coordSize = 2;
799    uint32_t uvSize = 2;
800    uint32_t vertsPerQuad = 4;
801    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
802    mTextMeshPtr = new float[vertexBufferSize];
803}
804
805// We don't want to allocate anything unless we actually draw text
806void FontRenderer::checkInit() {
807    if (mInitialized) {
808        return;
809    }
810
811    initTextTexture();
812    initVertexArrayBuffers();
813
814    mInitialized = true;
815}
816
817void FontRenderer::checkTextureUpdate() {
818    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
819        return;
820    }
821
822    Caches& caches = Caches::getInstance();
823    GLuint lastTextureId = 0;
824    // Iterate over all the cache lines and see which ones need to be updated
825    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
826        CacheTextureLine* cl = mCacheLines[i];
827        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
828            CacheTexture* cacheTexture = cl->mCacheTexture;
829            uint32_t xOffset = 0;
830            uint32_t yOffset = cl->mCurrentRow;
831            uint32_t width   = cl->mMaxWidth;
832            uint32_t height  = cl->mMaxHeight;
833            void* textureData = cacheTexture->mTexture + (yOffset * width);
834
835            if (cacheTexture->mTextureId != lastTextureId) {
836                caches.activeTexture(0);
837                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
838                lastTextureId = cacheTexture->mTextureId;
839            }
840            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
841                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
842
843            cl->mDirty = false;
844        }
845    }
846
847    caches.activeTexture(0);
848    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
849    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
850        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
851        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
852        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
853        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
854    }
855    mLastCacheTexture = mCurrentCacheTexture;
856
857    mUploadTexture = false;
858}
859
860void FontRenderer::issueDrawCommand() {
861    checkTextureUpdate();
862
863    Caches& caches = Caches::getInstance();
864    caches.bindIndicesBuffer(mIndexBufferID);
865    if (!mDrawn) {
866        float* buffer = mTextMeshPtr;
867        int offset = 2;
868
869        bool force = caches.unbindMeshBuffer();
870        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
871        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
872                buffer + offset);
873    }
874
875    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
876
877    mDrawn = true;
878}
879
880void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
881        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
882        float x4, float y4, float u4, float v4, CacheTexture* texture) {
883    if (texture != mCurrentCacheTexture) {
884        if (mCurrentQuadIndex != 0) {
885            // First, draw everything stored already which uses the previous texture
886            issueDrawCommand();
887            mCurrentQuadIndex = 0;
888        }
889        // Now use the new texture id
890        mCurrentCacheTexture = texture;
891    }
892
893    const uint32_t vertsPerQuad = 4;
894    const uint32_t floatsPerVert = 4;
895    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
896
897    (*currentPos++) = x1;
898    (*currentPos++) = y1;
899    (*currentPos++) = u1;
900    (*currentPos++) = v1;
901
902    (*currentPos++) = x2;
903    (*currentPos++) = y2;
904    (*currentPos++) = u2;
905    (*currentPos++) = v2;
906
907    (*currentPos++) = x3;
908    (*currentPos++) = y3;
909    (*currentPos++) = u3;
910    (*currentPos++) = v3;
911
912    (*currentPos++) = x4;
913    (*currentPos++) = y4;
914    (*currentPos++) = u4;
915    (*currentPos++) = v4;
916
917    mCurrentQuadIndex++;
918}
919
920void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
921        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
922        float x4, float y4, float u4, float v4, CacheTexture* texture) {
923
924    if (mClip &&
925            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
926        return;
927    }
928
929    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
930
931    if (mBounds) {
932        mBounds->left = fmin(mBounds->left, x1);
933        mBounds->top = fmin(mBounds->top, y3);
934        mBounds->right = fmax(mBounds->right, x3);
935        mBounds->bottom = fmax(mBounds->bottom, y1);
936    }
937
938    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
939        issueDrawCommand();
940        mCurrentQuadIndex = 0;
941    }
942}
943
944void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
945        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
946        float x4, float y4, float u4, float v4, CacheTexture* texture) {
947
948    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
949
950    if (mBounds) {
951        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
952        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
953        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
954        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
955    }
956
957    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
958        issueDrawCommand();
959        mCurrentQuadIndex = 0;
960    }
961}
962
963uint32_t FontRenderer::getRemainingCacheCapacity() {
964    uint32_t remainingCapacity = 0;
965    float totalPixels = 0;
966
967    //avoid divide by zero if the size is 0
968    if (mCacheLines.size() == 0) {
969        return 0;
970    }
971    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
972         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
973         totalPixels += mCacheLines[i]->mMaxWidth;
974    }
975    remainingCapacity = (remainingCapacity * 100) / totalPixels;
976    return remainingCapacity;
977}
978
979void FontRenderer::precacheLatin(SkPaint* paint) {
980    // Remaining capacity is measured in %
981    uint32_t remainingCapacity = getRemainingCacheCapacity();
982    uint32_t precacheIndex = 0;
983
984    // We store a string with letters in a rough frequency of occurrence
985    String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
986
987    size_t size = l.size();
988    uint16_t latin[size];
989    paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
990
991    while (remainingCapacity > 25 && precacheIndex < size) {
992        mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
993        remainingCapacity = getRemainingCacheCapacity();
994        precacheIndex++;
995    }
996}
997
998void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
999    uint32_t currentNumFonts = mActiveFonts.size();
1000    int flags = 0;
1001    if (paint->isFakeBoldText()) {
1002        flags |= Font::kFakeBold;
1003    }
1004
1005    const float skewX = paint->getTextSkewX();
1006    uint32_t italicStyle = *(uint32_t*) &skewX;
1007    const float scaleXFloat = paint->getTextScaleX();
1008    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1009    SkPaint::Style style = paint->getStyle();
1010    const float strokeWidthFloat = paint->getStrokeWidth();
1011    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1012    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1013            scaleX, style, strokeWidth);
1014
1015    const float maxPrecacheFontSize = 40.0f;
1016    bool isNewFont = currentNumFonts != mActiveFonts.size();
1017
1018    if (isNewFont && fontSize <= maxPrecacheFontSize) {
1019        precacheLatin(paint);
1020    }
1021}
1022
1023FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1024        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
1025    checkInit();
1026
1027    if (!mCurrentFont) {
1028        DropShadow image;
1029        image.width = 0;
1030        image.height = 0;
1031        image.image = NULL;
1032        image.penX = 0;
1033        image.penY = 0;
1034        return image;
1035    }
1036
1037    mDrawn = false;
1038    mClip = NULL;
1039    mBounds = NULL;
1040
1041    Rect bounds;
1042    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1043
1044    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1045    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1046    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1047
1048    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1049        dataBuffer[i] = 0;
1050    }
1051
1052    int penX = radius - bounds.left;
1053    int penY = radius - bounds.bottom;
1054
1055    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1056            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1057    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1058
1059    DropShadow image;
1060    image.width = paddedWidth;
1061    image.height = paddedHeight;
1062    image.image = dataBuffer;
1063    image.penX = penX;
1064    image.penY = penY;
1065
1066    return image;
1067}
1068
1069void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1070    checkInit();
1071
1072    mDrawn = false;
1073    mBounds = bounds;
1074    mClip = clip;
1075}
1076
1077void FontRenderer::finishRender() {
1078    mBounds = NULL;
1079    mClip = NULL;
1080
1081    if (mCurrentQuadIndex != 0) {
1082        issueDrawCommand();
1083        mCurrentQuadIndex = 0;
1084    }
1085}
1086
1087bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1088        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1089    if (!mCurrentFont) {
1090        ALOGE("No font set");
1091        return false;
1092    }
1093
1094    initRender(clip, bounds);
1095    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1096    finishRender();
1097
1098    return mDrawn;
1099}
1100
1101bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1102        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1103        const float* positions, Rect* bounds) {
1104    if (!mCurrentFont) {
1105        ALOGE("No font set");
1106        return false;
1107    }
1108
1109    initRender(clip, bounds);
1110    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1111    finishRender();
1112
1113    return mDrawn;
1114}
1115
1116bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1117        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1118        float hOffset, float vOffset, Rect* bounds) {
1119    if (!mCurrentFont) {
1120        ALOGE("No font set");
1121        return false;
1122    }
1123
1124    initRender(clip, bounds);
1125    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1126    finishRender();
1127
1128    return mDrawn;
1129}
1130
1131void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1132    // Compute gaussian weights for the blur
1133    // e is the euler's number
1134    float e = 2.718281828459045f;
1135    float pi = 3.1415926535897932f;
1136    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1137    // x is of the form [-radius .. 0 .. radius]
1138    // and sigma varies with radius.
1139    // Based on some experimental radius values and sigma's
1140    // we approximately fit sigma = f(radius) as
1141    // sigma = radius * 0.3  + 0.6
1142    // The larger the radius gets, the more our gaussian blur
1143    // will resemble a box blur since with large sigma
1144    // the gaussian curve begins to lose its shape
1145    float sigma = 0.3f * (float) radius + 0.6f;
1146
1147    // Now compute the coefficints
1148    // We will store some redundant values to save some math during
1149    // the blur calculations
1150    // precompute some values
1151    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1152    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1153
1154    float normalizeFactor = 0.0f;
1155    for (int32_t r = -radius; r <= radius; r ++) {
1156        float floatR = (float) r;
1157        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1158        normalizeFactor += weights[r + radius];
1159    }
1160
1161    //Now we need to normalize the weights because all our coefficients need to add up to one
1162    normalizeFactor = 1.0f / normalizeFactor;
1163    for (int32_t r = -radius; r <= radius; r ++) {
1164        weights[r + radius] *= normalizeFactor;
1165    }
1166}
1167
1168void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1169        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1170    float blurredPixel = 0.0f;
1171    float currentPixel = 0.0f;
1172
1173    for (int32_t y = 0; y < height; y ++) {
1174
1175        const uint8_t* input = source + y * width;
1176        uint8_t* output = dest + y * width;
1177
1178        for (int32_t x = 0; x < width; x ++) {
1179            blurredPixel = 0.0f;
1180            const float* gPtr = weights;
1181            // Optimization for non-border pixels
1182            if (x > radius && x < (width - radius)) {
1183                const uint8_t *i = input + (x - radius);
1184                for (int r = -radius; r <= radius; r ++) {
1185                    currentPixel = (float) (*i);
1186                    blurredPixel += currentPixel * gPtr[0];
1187                    gPtr++;
1188                    i++;
1189                }
1190            } else {
1191                for (int32_t r = -radius; r <= radius; r ++) {
1192                    // Stepping left and right away from the pixel
1193                    int validW = x + r;
1194                    if (validW < 0) {
1195                        validW = 0;
1196                    }
1197                    if (validW > width - 1) {
1198                        validW = width - 1;
1199                    }
1200
1201                    currentPixel = (float) input[validW];
1202                    blurredPixel += currentPixel * gPtr[0];
1203                    gPtr++;
1204                }
1205            }
1206            *output = (uint8_t)blurredPixel;
1207            output ++;
1208        }
1209    }
1210}
1211
1212void FontRenderer::verticalBlur(float* weights, int32_t radius,
1213        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1214    float blurredPixel = 0.0f;
1215    float currentPixel = 0.0f;
1216
1217    for (int32_t y = 0; y < height; y ++) {
1218
1219        uint8_t* output = dest + y * width;
1220
1221        for (int32_t x = 0; x < width; x ++) {
1222            blurredPixel = 0.0f;
1223            const float* gPtr = weights;
1224            const uint8_t* input = source + x;
1225            // Optimization for non-border pixels
1226            if (y > radius && y < (height - radius)) {
1227                const uint8_t *i = input + ((y - radius) * width);
1228                for (int32_t r = -radius; r <= radius; r ++) {
1229                    currentPixel = (float)(*i);
1230                    blurredPixel += currentPixel * gPtr[0];
1231                    gPtr++;
1232                    i += width;
1233                }
1234            } else {
1235                for (int32_t r = -radius; r <= radius; r ++) {
1236                    int validH = y + r;
1237                    // Clamp to zero and width
1238                    if (validH < 0) {
1239                        validH = 0;
1240                    }
1241                    if (validH > height - 1) {
1242                        validH = height - 1;
1243                    }
1244
1245                    const uint8_t *i = input + validH * width;
1246                    currentPixel = (float) (*i);
1247                    blurredPixel += currentPixel * gPtr[0];
1248                    gPtr++;
1249                }
1250            }
1251            *output = (uint8_t) blurredPixel;
1252            output ++;
1253        }
1254    }
1255}
1256
1257
1258void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1259    float *gaussian = new float[2 * radius + 1];
1260    computeGaussianWeights(gaussian, radius);
1261
1262    uint8_t* scratch = new uint8_t[width * height];
1263
1264    horizontalBlur(gaussian, radius, image, scratch, width, height);
1265    verticalBlur(gaussian, radius, scratch, image, width, height);
1266
1267    delete[] gaussian;
1268    delete[] scratch;
1269}
1270
1271}; // namespace uirenderer
1272}; // namespace android
1273