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