FontRenderer.cpp revision ae91c4cbc79ea910753be65e2f1d7899abcb4da2
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    mInitialized = true;
780}
781
782void FontRenderer::checkTextureUpdate() {
783    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
784        return;
785    }
786
787    Caches& caches = Caches::getInstance();
788    GLuint lastTextureId = 0;
789    // Iterate over all the cache lines and see which ones need to be updated
790    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
791        CacheTextureLine* cl = mCacheLines[i];
792        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
793            CacheTexture* cacheTexture = cl->mCacheTexture;
794            uint32_t xOffset = 0;
795            uint32_t yOffset = cl->mCurrentRow;
796            uint32_t width   = cl->mMaxWidth;
797            uint32_t height  = cl->mMaxHeight;
798            void* textureData = cacheTexture->mTexture + (yOffset * width);
799
800            if (cacheTexture->mTextureId != lastTextureId) {
801                caches.activeTexture(0);
802                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
803                lastTextureId = cacheTexture->mTextureId;
804            }
805            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
806                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
807
808            cl->mDirty = false;
809        }
810    }
811
812    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
813    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
814        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
815        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
816        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
817        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
818    }
819    mLastCacheTexture = mCurrentCacheTexture;
820
821    mUploadTexture = false;
822}
823
824void FontRenderer::issueDrawCommand() {
825    checkTextureUpdate();
826
827    Caches& caches = Caches::getInstance();
828    caches.bindIndicesBuffer(mIndexBufferID);
829    if (!mDrawn) {
830        float* buffer = mTextMeshPtr;
831        int offset = 2;
832
833        bool force = caches.unbindMeshBuffer();
834        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
835        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
836                buffer + offset);
837    }
838
839    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
840
841    mDrawn = true;
842}
843
844void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
845        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
846        float x4, float y4, float u4, float v4, CacheTexture* texture) {
847    if (texture != mCurrentCacheTexture) {
848        if (mCurrentQuadIndex != 0) {
849            // First, draw everything stored already which uses the previous texture
850            issueDrawCommand();
851            mCurrentQuadIndex = 0;
852        }
853        // Now use the new texture id
854        mCurrentCacheTexture = texture;
855    }
856
857    const uint32_t vertsPerQuad = 4;
858    const uint32_t floatsPerVert = 4;
859    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
860
861    (*currentPos++) = x1;
862    (*currentPos++) = y1;
863    (*currentPos++) = u1;
864    (*currentPos++) = v1;
865
866    (*currentPos++) = x2;
867    (*currentPos++) = y2;
868    (*currentPos++) = u2;
869    (*currentPos++) = v2;
870
871    (*currentPos++) = x3;
872    (*currentPos++) = y3;
873    (*currentPos++) = u3;
874    (*currentPos++) = v3;
875
876    (*currentPos++) = x4;
877    (*currentPos++) = y4;
878    (*currentPos++) = u4;
879    (*currentPos++) = v4;
880
881    mCurrentQuadIndex++;
882}
883
884void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
885        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
886        float x4, float y4, float u4, float v4, CacheTexture* texture) {
887
888    if (mClip &&
889            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
890        return;
891    }
892
893    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
894
895    if (mBounds) {
896        mBounds->left = fmin(mBounds->left, x1);
897        mBounds->top = fmin(mBounds->top, y3);
898        mBounds->right = fmax(mBounds->right, x3);
899        mBounds->bottom = fmax(mBounds->bottom, y1);
900    }
901
902    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
903        issueDrawCommand();
904        mCurrentQuadIndex = 0;
905    }
906}
907
908void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
909        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
910        float x4, float y4, float u4, float v4, CacheTexture* texture) {
911
912    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
913
914    if (mBounds) {
915        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
916        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
917        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
918        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
919    }
920
921    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
922        issueDrawCommand();
923        mCurrentQuadIndex = 0;
924    }
925}
926
927uint32_t FontRenderer::getRemainingCacheCapacity() {
928    uint32_t remainingCapacity = 0;
929    float totalPixels = 0;
930    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
931         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
932         totalPixels += mCacheLines[i]->mMaxWidth;
933    }
934    remainingCapacity = (remainingCapacity * 100) / totalPixels;
935    return remainingCapacity;
936}
937
938void FontRenderer::precacheLatin(SkPaint* paint) {
939    // Remaining capacity is measured in %
940    uint32_t remainingCapacity = getRemainingCacheCapacity();
941    uint32_t precacheIndex = 0;
942
943    // We store a string with letters in a rough frequency of occurrence
944    String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
945
946    size_t size = l.size();
947    uint16_t latin[size];
948    paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
949
950    while (remainingCapacity > 25 && precacheIndex < size) {
951        mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
952        remainingCapacity = getRemainingCacheCapacity();
953        precacheIndex++;
954    }
955}
956
957void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
958    uint32_t currentNumFonts = mActiveFonts.size();
959    int flags = 0;
960    if (paint->isFakeBoldText()) {
961        flags |= Font::kFakeBold;
962    }
963
964    const float skewX = paint->getTextSkewX();
965    uint32_t italicStyle = *(uint32_t*) &skewX;
966    const float scaleXFloat = paint->getTextScaleX();
967    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
968    SkPaint::Style style = paint->getStyle();
969    const float strokeWidthFloat = paint->getStrokeWidth();
970    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
971    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
972            scaleX, style, strokeWidth);
973
974    const float maxPrecacheFontSize = 40.0f;
975    bool isNewFont = currentNumFonts != mActiveFonts.size();
976
977    if (isNewFont && fontSize <= maxPrecacheFontSize) {
978        precacheLatin(paint);
979    }
980}
981
982FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
983        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
984    checkInit();
985
986    if (!mCurrentFont) {
987        DropShadow image;
988        image.width = 0;
989        image.height = 0;
990        image.image = NULL;
991        image.penX = 0;
992        image.penY = 0;
993        return image;
994    }
995
996    mDrawn = false;
997    mClip = NULL;
998    mBounds = NULL;
999
1000    Rect bounds;
1001    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
1002
1003    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1004    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1005    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1006
1007    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1008        dataBuffer[i] = 0;
1009    }
1010
1011    int penX = radius - bounds.left;
1012    int penY = radius - bounds.bottom;
1013
1014    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1015            dataBuffer, paddedWidth, paddedHeight);
1016    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1017
1018    DropShadow image;
1019    image.width = paddedWidth;
1020    image.height = paddedHeight;
1021    image.image = dataBuffer;
1022    image.penX = penX;
1023    image.penY = penY;
1024
1025    return image;
1026}
1027
1028void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1029    checkInit();
1030
1031    mDrawn = false;
1032    mBounds = bounds;
1033    mClip = clip;
1034}
1035
1036void FontRenderer::finishRender() {
1037    mBounds = NULL;
1038    mClip = NULL;
1039
1040    if (mCurrentQuadIndex != 0) {
1041        issueDrawCommand();
1042        mCurrentQuadIndex = 0;
1043    }
1044}
1045
1046bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1047        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1048    if (!mCurrentFont) {
1049        ALOGE("No font set");
1050        return false;
1051    }
1052
1053    initRender(clip, bounds);
1054    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1055    finishRender();
1056
1057    return mDrawn;
1058}
1059
1060bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1061        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1062        const float* positions, Rect* bounds) {
1063    if (!mCurrentFont) {
1064        ALOGE("No font set");
1065        return false;
1066    }
1067
1068    initRender(clip, bounds);
1069    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1070    finishRender();
1071
1072    return mDrawn;
1073}
1074
1075bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1076        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1077        float hOffset, float vOffset, Rect* bounds) {
1078    if (!mCurrentFont) {
1079        ALOGE("No font set");
1080        return false;
1081    }
1082
1083    initRender(clip, bounds);
1084    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1085    finishRender();
1086
1087    return mDrawn;
1088}
1089
1090void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1091    // Compute gaussian weights for the blur
1092    // e is the euler's number
1093    float e = 2.718281828459045f;
1094    float pi = 3.1415926535897932f;
1095    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1096    // x is of the form [-radius .. 0 .. radius]
1097    // and sigma varies with radius.
1098    // Based on some experimental radius values and sigma's
1099    // we approximately fit sigma = f(radius) as
1100    // sigma = radius * 0.3  + 0.6
1101    // The larger the radius gets, the more our gaussian blur
1102    // will resemble a box blur since with large sigma
1103    // the gaussian curve begins to lose its shape
1104    float sigma = 0.3f * (float) radius + 0.6f;
1105
1106    // Now compute the coefficints
1107    // We will store some redundant values to save some math during
1108    // the blur calculations
1109    // precompute some values
1110    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1111    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1112
1113    float normalizeFactor = 0.0f;
1114    for (int32_t r = -radius; r <= radius; r ++) {
1115        float floatR = (float) r;
1116        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1117        normalizeFactor += weights[r + radius];
1118    }
1119
1120    //Now we need to normalize the weights because all our coefficients need to add up to one
1121    normalizeFactor = 1.0f / normalizeFactor;
1122    for (int32_t r = -radius; r <= radius; r ++) {
1123        weights[r + radius] *= normalizeFactor;
1124    }
1125}
1126
1127void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1128        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1129    float blurredPixel = 0.0f;
1130    float currentPixel = 0.0f;
1131
1132    for (int32_t y = 0; y < height; y ++) {
1133
1134        const uint8_t* input = source + y * width;
1135        uint8_t* output = dest + y * width;
1136
1137        for (int32_t x = 0; x < width; x ++) {
1138            blurredPixel = 0.0f;
1139            const float* gPtr = weights;
1140            // Optimization for non-border pixels
1141            if (x > radius && x < (width - radius)) {
1142                const uint8_t *i = input + (x - radius);
1143                for (int r = -radius; r <= radius; r ++) {
1144                    currentPixel = (float) (*i);
1145                    blurredPixel += currentPixel * gPtr[0];
1146                    gPtr++;
1147                    i++;
1148                }
1149            } else {
1150                for (int32_t r = -radius; r <= radius; r ++) {
1151                    // Stepping left and right away from the pixel
1152                    int validW = x + r;
1153                    if (validW < 0) {
1154                        validW = 0;
1155                    }
1156                    if (validW > width - 1) {
1157                        validW = width - 1;
1158                    }
1159
1160                    currentPixel = (float) input[validW];
1161                    blurredPixel += currentPixel * gPtr[0];
1162                    gPtr++;
1163                }
1164            }
1165            *output = (uint8_t)blurredPixel;
1166            output ++;
1167        }
1168    }
1169}
1170
1171void FontRenderer::verticalBlur(float* weights, int32_t radius,
1172        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1173    float blurredPixel = 0.0f;
1174    float currentPixel = 0.0f;
1175
1176    for (int32_t y = 0; y < height; y ++) {
1177
1178        uint8_t* output = dest + y * width;
1179
1180        for (int32_t x = 0; x < width; x ++) {
1181            blurredPixel = 0.0f;
1182            const float* gPtr = weights;
1183            const uint8_t* input = source + x;
1184            // Optimization for non-border pixels
1185            if (y > radius && y < (height - radius)) {
1186                const uint8_t *i = input + ((y - radius) * width);
1187                for (int32_t r = -radius; r <= radius; r ++) {
1188                    currentPixel = (float)(*i);
1189                    blurredPixel += currentPixel * gPtr[0];
1190                    gPtr++;
1191                    i += width;
1192                }
1193            } else {
1194                for (int32_t r = -radius; r <= radius; r ++) {
1195                    int validH = y + r;
1196                    // Clamp to zero and width
1197                    if (validH < 0) {
1198                        validH = 0;
1199                    }
1200                    if (validH > height - 1) {
1201                        validH = height - 1;
1202                    }
1203
1204                    const uint8_t *i = input + validH * width;
1205                    currentPixel = (float) (*i);
1206                    blurredPixel += currentPixel * gPtr[0];
1207                    gPtr++;
1208                }
1209            }
1210            *output = (uint8_t) blurredPixel;
1211            output ++;
1212        }
1213    }
1214}
1215
1216
1217void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1218    float *gaussian = new float[2 * radius + 1];
1219    computeGaussianWeights(gaussian, radius);
1220
1221    uint8_t* scratch = new uint8_t[width * height];
1222
1223    horizontalBlur(gaussian, radius, image, scratch, width, height);
1224    verticalBlur(gaussian, radius, scratch, image, width, height);
1225
1226    delete[] gaussian;
1227    delete[] scratch;
1228}
1229
1230}; // namespace uirenderer
1231}; // namespace android
1232