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