FontRenderer.cpp revision 99a6ddd4cd8762654a575eb4ac3d0e5431d919b8
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 2
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 < mMaxWidth) {
54        *retOriginX = mCurrentCol + 1;
55        *retOriginY = mCurrentRow + 1;
56        mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
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) {
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, NULL);
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 = (float) startX / (float) cacheWidth;
416    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
417    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
418    glyph->mBitmapMaxV = (float) 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#if DEBUG_FONT_RENDERER
594    memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
595#endif
596
597    if (!cacheTexture->mTextureId) {
598        glGenTextures(1, &cacheTexture->mTextureId);
599    }
600
601    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
602    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
603    // Initialize texture dimensions
604    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
605            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
606
607    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
608    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
609    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
610
611    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
612    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
613}
614
615void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
616        uint32_t* retOriginX, uint32_t* retOriginY) {
617    cachedGlyph->mIsValid = false;
618    // If the glyph is too tall, don't cache it
619    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
620        ALOGE("Font size to large to fit in cache. width, height = %i, %i",
621                (int) glyph.fWidth, (int) glyph.fHeight);
622        return;
623    }
624
625    // Now copy the bitmap into the cache texture
626    uint32_t startX = 0;
627    uint32_t startY = 0;
628
629    bool bitmapFit = false;
630    CacheTextureLine *cacheLine;
631    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
632        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
633        if (bitmapFit) {
634            cacheLine = mCacheLines[i];
635            break;
636        }
637    }
638
639    // If the new glyph didn't fit, flush the state so far and invalidate everything
640    if (!bitmapFit) {
641        flushAllAndInvalidate();
642
643        // Try to fit it again
644        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
645            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
646            if (bitmapFit) {
647                cacheLine = mCacheLines[i];
648                break;
649            }
650        }
651
652        // if we still don't fit, something is wrong and we shouldn't draw
653        if (!bitmapFit) {
654            return;
655        }
656    }
657
658    cachedGlyph->mCachedTextureLine = cacheLine;
659
660    *retOriginX = startX;
661    *retOriginY = startY;
662
663    uint32_t endX = startX + glyph.fWidth;
664    uint32_t endY = startY + glyph.fHeight;
665
666    uint32_t cacheWidth = cacheLine->mMaxWidth;
667
668    CacheTexture* cacheTexture = cacheLine->mCacheTexture;
669    if (!cacheTexture->mTexture) {
670        // Large-glyph texture memory is allocated only as needed
671        allocateTextureMemory(cacheTexture);
672    }
673
674    uint8_t* cacheBuffer = cacheTexture->mTexture;
675    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
676    unsigned int stride = glyph.rowBytes();
677
678    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
679    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
680        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
681            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
682            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
683        }
684    }
685
686    cachedGlyph->mIsValid = true;
687}
688
689CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
690    uint8_t* textureMemory = NULL;
691    CacheTexture* cacheTexture = new CacheTexture(textureMemory, width, height);
692
693    if (allocate) {
694        allocateTextureMemory(cacheTexture);
695    }
696
697    return cacheTexture;
698}
699
700void FontRenderer::initTextTexture() {
701    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
702        delete mCacheLines[i];
703    }
704    mCacheLines.clear();
705
706    if (mCacheTextureSmall) {
707        delete mCacheTextureSmall;
708        delete mCacheTexture128;
709        delete mCacheTexture256;
710        delete mCacheTexture512;
711    }
712
713    // Next, use other, separate caches for large glyphs.
714    uint16_t maxWidth = 0;
715    if (Caches::hasInstance()) {
716        maxWidth = Caches::getInstance().maxTextureSize;
717    }
718
719    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
720        maxWidth = MAX_TEXT_CACHE_WIDTH;
721    }
722
723    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
724    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
725    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
726    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
727    mCurrentCacheTexture = mCacheTextureSmall;
728
729    mUploadTexture = false;
730    // Split up our default cache texture into lines of certain widths
731    int nextLine = 0;
732    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
733    nextLine += mCacheLines.top()->mMaxHeight;
734    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
735    nextLine += mCacheLines.top()->mMaxHeight;
736    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
737    nextLine += mCacheLines.top()->mMaxHeight;
738    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
739    nextLine += mCacheLines.top()->mMaxHeight;
740    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
741    nextLine += mCacheLines.top()->mMaxHeight;
742    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
743    nextLine += mCacheLines.top()->mMaxHeight;
744    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
745            nextLine, 0, mCacheTextureSmall));
746
747    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
748    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
749    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
750    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
751    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
752}
753
754// Avoid having to reallocate memory and render quad by quad
755void FontRenderer::initVertexArrayBuffers() {
756    uint32_t numIndices = mMaxNumberOfQuads * 6;
757    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
758    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
759
760    // Four verts, two triangles , six indices per quad
761    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
762        int i6 = i * 6;
763        int i4 = i * 4;
764
765        indexBufferData[i6 + 0] = i4 + 0;
766        indexBufferData[i6 + 1] = i4 + 1;
767        indexBufferData[i6 + 2] = i4 + 2;
768
769        indexBufferData[i6 + 3] = i4 + 0;
770        indexBufferData[i6 + 4] = i4 + 2;
771        indexBufferData[i6 + 5] = i4 + 3;
772    }
773
774    glGenBuffers(1, &mIndexBufferID);
775    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
776    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
777
778    free(indexBufferData);
779
780    uint32_t coordSize = 2;
781    uint32_t uvSize = 2;
782    uint32_t vertsPerQuad = 4;
783    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
784    mTextMeshPtr = new float[vertexBufferSize];
785}
786
787// We don't want to allocate anything unless we actually draw text
788void FontRenderer::checkInit() {
789    if (mInitialized) {
790        return;
791    }
792
793    initTextTexture();
794    initVertexArrayBuffers();
795
796    mInitialized = true;
797}
798
799void FontRenderer::checkTextureUpdate() {
800    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
801        return;
802    }
803
804    Caches& caches = Caches::getInstance();
805    GLuint lastTextureId = 0;
806    // Iterate over all the cache lines and see which ones need to be updated
807    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
808        CacheTextureLine* cl = mCacheLines[i];
809        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
810            CacheTexture* cacheTexture = cl->mCacheTexture;
811            uint32_t xOffset = 0;
812            uint32_t yOffset = cl->mCurrentRow;
813            uint32_t width   = cl->mMaxWidth;
814            uint32_t height  = cl->mMaxHeight;
815            void* textureData = cacheTexture->mTexture + (yOffset * width);
816
817            if (cacheTexture->mTextureId != lastTextureId) {
818                caches.activeTexture(0);
819                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
820                lastTextureId = cacheTexture->mTextureId;
821            }
822            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
823                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
824
825            cl->mDirty = false;
826        }
827    }
828
829    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
830    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
831        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
832        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
833        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
834        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
835    }
836    mLastCacheTexture = mCurrentCacheTexture;
837
838    mUploadTexture = false;
839}
840
841void FontRenderer::issueDrawCommand() {
842    checkTextureUpdate();
843
844    Caches& caches = Caches::getInstance();
845    caches.bindIndicesBuffer(mIndexBufferID);
846    if (!mDrawn) {
847        float* buffer = mTextMeshPtr;
848        int offset = 2;
849
850        bool force = caches.unbindMeshBuffer();
851        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
852        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
853                buffer + offset);
854    }
855
856    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
857
858    mDrawn = true;
859}
860
861void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
862        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
863        float x4, float y4, float u4, float v4, CacheTexture* texture) {
864    if (texture != mCurrentCacheTexture) {
865        if (mCurrentQuadIndex != 0) {
866            // First, draw everything stored already which uses the previous texture
867            issueDrawCommand();
868            mCurrentQuadIndex = 0;
869        }
870        // Now use the new texture id
871        mCurrentCacheTexture = texture;
872    }
873
874    const uint32_t vertsPerQuad = 4;
875    const uint32_t floatsPerVert = 4;
876    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
877
878    (*currentPos++) = x1;
879    (*currentPos++) = y1;
880    (*currentPos++) = u1;
881    (*currentPos++) = v1;
882
883    (*currentPos++) = x2;
884    (*currentPos++) = y2;
885    (*currentPos++) = u2;
886    (*currentPos++) = v2;
887
888    (*currentPos++) = x3;
889    (*currentPos++) = y3;
890    (*currentPos++) = u3;
891    (*currentPos++) = v3;
892
893    (*currentPos++) = x4;
894    (*currentPos++) = y4;
895    (*currentPos++) = u4;
896    (*currentPos++) = v4;
897
898    mCurrentQuadIndex++;
899}
900
901void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
902        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
903        float x4, float y4, float u4, float v4, CacheTexture* texture) {
904
905    if (mClip &&
906            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
907        return;
908    }
909
910    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
911
912    if (mBounds) {
913        mBounds->left = fmin(mBounds->left, x1);
914        mBounds->top = fmin(mBounds->top, y3);
915        mBounds->right = fmax(mBounds->right, x3);
916        mBounds->bottom = fmax(mBounds->bottom, y1);
917    }
918
919    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
920        issueDrawCommand();
921        mCurrentQuadIndex = 0;
922    }
923}
924
925void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
926        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
927        float x4, float y4, float u4, float v4, CacheTexture* texture) {
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, fmin(x1, fmin(x2, fmin(x3, x4))));
933        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
934        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
935        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
936    }
937
938    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
939        issueDrawCommand();
940        mCurrentQuadIndex = 0;
941    }
942}
943
944uint32_t FontRenderer::getRemainingCacheCapacity() {
945    uint32_t remainingCapacity = 0;
946    float totalPixels = 0;
947    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
948         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
949         totalPixels += mCacheLines[i]->mMaxWidth;
950    }
951    remainingCapacity = (remainingCapacity * 100) / totalPixels;
952    return remainingCapacity;
953}
954
955void FontRenderer::precacheLatin(SkPaint* paint) {
956    // Remaining capacity is measured in %
957    uint32_t remainingCapacity = getRemainingCacheCapacity();
958    uint32_t precacheIndex = 0;
959
960    // We store a string with letters in a rough frequency of occurrence
961    String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
962
963    size_t size = l.size();
964    uint16_t latin[size];
965    paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
966
967    while (remainingCapacity > 25 && precacheIndex < size) {
968        mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
969        remainingCapacity = getRemainingCacheCapacity();
970        precacheIndex++;
971    }
972}
973
974void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
975    uint32_t currentNumFonts = mActiveFonts.size();
976    int flags = 0;
977    if (paint->isFakeBoldText()) {
978        flags |= Font::kFakeBold;
979    }
980
981    const float skewX = paint->getTextSkewX();
982    uint32_t italicStyle = *(uint32_t*) &skewX;
983    const float scaleXFloat = paint->getTextScaleX();
984    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
985    SkPaint::Style style = paint->getStyle();
986    const float strokeWidthFloat = paint->getStrokeWidth();
987    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
988    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
989            scaleX, style, strokeWidth);
990
991    const float maxPrecacheFontSize = 40.0f;
992    bool isNewFont = currentNumFonts != mActiveFonts.size();
993
994    if (isNewFont && fontSize <= maxPrecacheFontSize) {
995        precacheLatin(paint);
996    }
997}
998
999FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1000        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
1001    checkInit();
1002
1003    if (!mCurrentFont) {
1004        DropShadow image;
1005        image.width = 0;
1006        image.height = 0;
1007        image.image = NULL;
1008        image.penX = 0;
1009        image.penY = 0;
1010        return image;
1011    }
1012
1013    mDrawn = false;
1014    mClip = NULL;
1015    mBounds = NULL;
1016
1017    Rect bounds;
1018    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
1019
1020    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1021    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1022    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1023
1024    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1025        dataBuffer[i] = 0;
1026    }
1027
1028    int penX = radius - bounds.left;
1029    int penY = radius - bounds.bottom;
1030
1031    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1032            dataBuffer, paddedWidth, paddedHeight);
1033    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1034
1035    DropShadow image;
1036    image.width = paddedWidth;
1037    image.height = paddedHeight;
1038    image.image = dataBuffer;
1039    image.penX = penX;
1040    image.penY = penY;
1041
1042    return image;
1043}
1044
1045void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1046    checkInit();
1047
1048    mDrawn = false;
1049    mBounds = bounds;
1050    mClip = clip;
1051}
1052
1053void FontRenderer::finishRender() {
1054    mBounds = NULL;
1055    mClip = NULL;
1056
1057    if (mCurrentQuadIndex != 0) {
1058        issueDrawCommand();
1059        mCurrentQuadIndex = 0;
1060    }
1061}
1062
1063bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1064        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1065    if (!mCurrentFont) {
1066        ALOGE("No font set");
1067        return false;
1068    }
1069
1070    initRender(clip, bounds);
1071    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1072    finishRender();
1073
1074    return mDrawn;
1075}
1076
1077bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1078        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1079        const float* positions, Rect* bounds) {
1080    if (!mCurrentFont) {
1081        ALOGE("No font set");
1082        return false;
1083    }
1084
1085    initRender(clip, bounds);
1086    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1087    finishRender();
1088
1089    return mDrawn;
1090}
1091
1092bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1093        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1094        float hOffset, float vOffset, Rect* bounds) {
1095    if (!mCurrentFont) {
1096        ALOGE("No font set");
1097        return false;
1098    }
1099
1100    initRender(clip, bounds);
1101    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1102    finishRender();
1103
1104    return mDrawn;
1105}
1106
1107void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1108    // Compute gaussian weights for the blur
1109    // e is the euler's number
1110    float e = 2.718281828459045f;
1111    float pi = 3.1415926535897932f;
1112    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1113    // x is of the form [-radius .. 0 .. radius]
1114    // and sigma varies with radius.
1115    // Based on some experimental radius values and sigma's
1116    // we approximately fit sigma = f(radius) as
1117    // sigma = radius * 0.3  + 0.6
1118    // The larger the radius gets, the more our gaussian blur
1119    // will resemble a box blur since with large sigma
1120    // the gaussian curve begins to lose its shape
1121    float sigma = 0.3f * (float) radius + 0.6f;
1122
1123    // Now compute the coefficints
1124    // We will store some redundant values to save some math during
1125    // the blur calculations
1126    // precompute some values
1127    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1128    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1129
1130    float normalizeFactor = 0.0f;
1131    for (int32_t r = -radius; r <= radius; r ++) {
1132        float floatR = (float) r;
1133        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1134        normalizeFactor += weights[r + radius];
1135    }
1136
1137    //Now we need to normalize the weights because all our coefficients need to add up to one
1138    normalizeFactor = 1.0f / normalizeFactor;
1139    for (int32_t r = -radius; r <= radius; r ++) {
1140        weights[r + radius] *= normalizeFactor;
1141    }
1142}
1143
1144void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1145        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1146    float blurredPixel = 0.0f;
1147    float currentPixel = 0.0f;
1148
1149    for (int32_t y = 0; y < height; y ++) {
1150
1151        const uint8_t* input = source + y * width;
1152        uint8_t* output = dest + y * width;
1153
1154        for (int32_t x = 0; x < width; x ++) {
1155            blurredPixel = 0.0f;
1156            const float* gPtr = weights;
1157            // Optimization for non-border pixels
1158            if (x > radius && x < (width - radius)) {
1159                const uint8_t *i = input + (x - radius);
1160                for (int r = -radius; r <= radius; r ++) {
1161                    currentPixel = (float) (*i);
1162                    blurredPixel += currentPixel * gPtr[0];
1163                    gPtr++;
1164                    i++;
1165                }
1166            } else {
1167                for (int32_t r = -radius; r <= radius; r ++) {
1168                    // Stepping left and right away from the pixel
1169                    int validW = x + r;
1170                    if (validW < 0) {
1171                        validW = 0;
1172                    }
1173                    if (validW > width - 1) {
1174                        validW = width - 1;
1175                    }
1176
1177                    currentPixel = (float) input[validW];
1178                    blurredPixel += currentPixel * gPtr[0];
1179                    gPtr++;
1180                }
1181            }
1182            *output = (uint8_t)blurredPixel;
1183            output ++;
1184        }
1185    }
1186}
1187
1188void FontRenderer::verticalBlur(float* weights, int32_t radius,
1189        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1190    float blurredPixel = 0.0f;
1191    float currentPixel = 0.0f;
1192
1193    for (int32_t y = 0; y < height; y ++) {
1194
1195        uint8_t* output = dest + y * width;
1196
1197        for (int32_t x = 0; x < width; x ++) {
1198            blurredPixel = 0.0f;
1199            const float* gPtr = weights;
1200            const uint8_t* input = source + x;
1201            // Optimization for non-border pixels
1202            if (y > radius && y < (height - radius)) {
1203                const uint8_t *i = input + ((y - radius) * width);
1204                for (int32_t r = -radius; r <= radius; r ++) {
1205                    currentPixel = (float)(*i);
1206                    blurredPixel += currentPixel * gPtr[0];
1207                    gPtr++;
1208                    i += width;
1209                }
1210            } else {
1211                for (int32_t r = -radius; r <= radius; r ++) {
1212                    int validH = y + r;
1213                    // Clamp to zero and width
1214                    if (validH < 0) {
1215                        validH = 0;
1216                    }
1217                    if (validH > height - 1) {
1218                        validH = height - 1;
1219                    }
1220
1221                    const uint8_t *i = input + validH * width;
1222                    currentPixel = (float) (*i);
1223                    blurredPixel += currentPixel * gPtr[0];
1224                    gPtr++;
1225                }
1226            }
1227            *output = (uint8_t) blurredPixel;
1228            output ++;
1229        }
1230    }
1231}
1232
1233
1234void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1235    float *gaussian = new float[2 * radius + 1];
1236    computeGaussianWeights(gaussian, radius);
1237
1238    uint8_t* scratch = new uint8_t[width * height];
1239
1240    horizontalBlur(gaussian, radius, image, scratch, width, height);
1241    verticalBlur(gaussian, radius, scratch, image, width, height);
1242
1243    delete[] gaussian;
1244    delete[] scratch;
1245}
1246
1247}; // namespace uirenderer
1248}; // namespace android
1249