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