FontRenderer.cpp revision 416a847633680d94efb926837efdc18726d54918
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, const float* positions) {
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, positions);
298}
299
300void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
301        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
302        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
303    if (numGlyphs == 0 || text == NULL || len == 0) {
304        return;
305    }
306
307    static RenderGlyph gRenderGlyph[] = {
308            &android::uirenderer::Font::drawCachedGlyph,
309            &android::uirenderer::Font::drawCachedGlyphBitmap,
310            &android::uirenderer::Font::measureCachedGlyph
311    };
312    RenderGlyph render = gRenderGlyph[mode];
313
314    text += start;
315    int glyphsCount = 0;
316
317    if (CC_LIKELY(positions == NULL)) {
318        SkFixed prevRsbDelta = 0;
319
320        float penX = x + 0.5f;
321        int penY = y;
322
323        while (glyphsCount < numGlyphs) {
324            glyph_t glyph = GET_GLYPH(text);
325
326            // Reached the end of the string
327            if (IS_END_OF_STRING(glyph)) {
328                break;
329            }
330
331            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
332            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
333            prevRsbDelta = cachedGlyph->mRsbDelta;
334
335            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
336            if (cachedGlyph->mIsValid) {
337                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
338                        bitmap, bitmapW, bitmapH, bounds, positions);
339            }
340
341            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
342
343            glyphsCount++;
344        }
345    } else {
346        const SkPaint::Align align = paint->getTextAlign();
347
348        // This is for renderPosText()
349        while (glyphsCount < numGlyphs) {
350            glyph_t glyph = GET_GLYPH(text);
351
352            // Reached the end of the string
353            if (IS_END_OF_STRING(glyph)) {
354                break;
355            }
356
357            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
358
359            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
360            if (cachedGlyph->mIsValid) {
361                int penX = x + positions[(glyphsCount << 1)];
362                int penY = y + positions[(glyphsCount << 1) + 1];
363
364                switch (align) {
365                    case SkPaint::kRight_Align:
366                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
367                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
368                        break;
369                    case SkPaint::kCenter_Align:
370                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
371                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
372                    default:
373                        break;
374                }
375
376                (*this.*render)(cachedGlyph, penX, penY,
377                        bitmap, bitmapW, bitmapH, bounds, positions);
378            }
379
380            glyphsCount++;
381        }
382    }
383}
384
385void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
386    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
387    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
388    glyph->mBitmapLeft = skiaGlyph.fLeft;
389    glyph->mBitmapTop = skiaGlyph.fTop;
390    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
391    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
392
393    uint32_t startX = 0;
394    uint32_t startY = 0;
395
396    // Get the bitmap for the glyph
397    paint->findImage(skiaGlyph);
398    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
399
400    if (!glyph->mIsValid) {
401        return;
402    }
403
404    uint32_t endX = startX + skiaGlyph.fWidth;
405    uint32_t endY = startY + skiaGlyph.fHeight;
406
407    glyph->mStartX = startX;
408    glyph->mStartY = startY;
409    glyph->mBitmapWidth = skiaGlyph.fWidth;
410    glyph->mBitmapHeight = skiaGlyph.fHeight;
411
412    uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
413    uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
414
415    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
416    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
417    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
418    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
419
420    mState->mUploadTexture = true;
421}
422
423CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
424    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
425    mCachedGlyphs.add(glyph, newGlyph);
426
427    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
428    newGlyph->mGlyphIndex = skiaGlyph.fID;
429    newGlyph->mIsValid = false;
430
431    updateGlyphCache(paint, skiaGlyph, newGlyph);
432
433    return newGlyph;
434}
435
436Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
437        int flags, uint32_t italicStyle, uint32_t scaleX,
438        SkPaint::Style style, uint32_t strokeWidth) {
439    Vector<Font*> &activeFonts = state->mActiveFonts;
440
441    for (uint32_t i = 0; i < activeFonts.size(); i++) {
442        Font* font = activeFonts[i];
443        if (font->mFontId == fontId && font->mFontSize == fontSize &&
444                font->mFlags == flags && font->mItalicStyle == italicStyle &&
445                font->mScaleX == scaleX && font->mStyle == style &&
446                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
447            return font;
448        }
449    }
450
451    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
452            scaleX, style, strokeWidth);
453    activeFonts.push(newFont);
454    return newFont;
455}
456
457///////////////////////////////////////////////////////////////////////////////
458// FontRenderer
459///////////////////////////////////////////////////////////////////////////////
460
461static bool sLogFontRendererCreate = true;
462
463FontRenderer::FontRenderer() {
464    if (sLogFontRendererCreate) {
465        INIT_LOGD("Creating FontRenderer");
466    }
467
468    mGammaTable = NULL;
469    mInitialized = false;
470    mMaxNumberOfQuads = 1024;
471    mCurrentQuadIndex = 0;
472
473    mTextMeshPtr = NULL;
474    mCurrentCacheTexture = NULL;
475    mLastCacheTexture = NULL;
476    mCacheTextureSmall = NULL;
477    mCacheTexture128 = NULL;
478    mCacheTexture256 = NULL;
479    mCacheTexture512 = NULL;
480
481    mLinearFiltering = false;
482
483    mIndexBufferID = 0;
484
485    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
486    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
487
488    char property[PROPERTY_VALUE_MAX];
489    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
490        if (sLogFontRendererCreate) {
491            INIT_LOGD("  Setting text cache width to %s pixels", property);
492        }
493        mSmallCacheWidth = atoi(property);
494    } else {
495        if (sLogFontRendererCreate) {
496            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
497        }
498    }
499
500    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
501        if (sLogFontRendererCreate) {
502            INIT_LOGD("  Setting text cache width to %s pixels", property);
503        }
504        mSmallCacheHeight = atoi(property);
505    } else {
506        if (sLogFontRendererCreate) {
507            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
508        }
509    }
510
511    sLogFontRendererCreate = false;
512}
513
514FontRenderer::~FontRenderer() {
515    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
516        delete mCacheLines[i];
517    }
518    mCacheLines.clear();
519
520    if (mInitialized) {
521        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
522        Caches::getInstance().unbindIndicesBuffer();
523        glDeleteBuffers(1, &mIndexBufferID);
524
525        delete[] mTextMeshPtr;
526        delete mCacheTextureSmall;
527        delete mCacheTexture128;
528        delete mCacheTexture256;
529        delete mCacheTexture512;
530    }
531
532    Vector<Font*> fontsToDereference = mActiveFonts;
533    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
534        delete fontsToDereference[i];
535    }
536}
537
538void FontRenderer::flushAllAndInvalidate() {
539    if (mCurrentQuadIndex != 0) {
540        issueDrawCommand();
541        mCurrentQuadIndex = 0;
542    }
543
544    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
545        mActiveFonts[i]->invalidateTextureCache();
546    }
547
548    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
549        mCacheLines[i]->mCurrentCol = 0;
550    }
551}
552
553void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
554    if (cacheTexture && cacheTexture->mTexture) {
555        glDeleteTextures(1, &cacheTexture->mTextureId);
556        delete[] cacheTexture->mTexture;
557        cacheTexture->mTexture = NULL;
558        cacheTexture->mTextureId = 0;
559    }
560}
561
562void FontRenderer::flushLargeCaches() {
563    if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
564            (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
565            (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
566        // Typical case; no large glyph caches allocated
567        return;
568    }
569
570    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
571        CacheTextureLine* cacheLine = mCacheLines[i];
572        if ((cacheLine->mCacheTexture == mCacheTexture128 ||
573                cacheLine->mCacheTexture == mCacheTexture256 ||
574                cacheLine->mCacheTexture == mCacheTexture512) &&
575                cacheLine->mCacheTexture->mTexture != NULL) {
576            cacheLine->mCurrentCol = 0;
577            for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
578                mActiveFonts[i]->invalidateTextureCache(cacheLine);
579            }
580        }
581    }
582
583    deallocateTextureMemory(mCacheTexture128);
584    deallocateTextureMemory(mCacheTexture256);
585    deallocateTextureMemory(mCacheTexture512);
586}
587
588void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
589    int width = cacheTexture->mWidth;
590    int height = cacheTexture->mHeight;
591
592    cacheTexture->mTexture = new uint8_t[width * height];
593#if DEBUG_FONT_RENDERER
594    memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
595#endif
596
597    if (!cacheTexture->mTextureId) {
598        glGenTextures(1, &cacheTexture->mTextureId);
599    }
600
601    Caches::getInstance().activeTexture(0);
602    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
603    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
604    // Initialize texture dimensions
605    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
606            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
607
608    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
609    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
610    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
611
612    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
613    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
614}
615
616void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
617        uint32_t* retOriginX, uint32_t* retOriginY) {
618    cachedGlyph->mIsValid = false;
619    // If the glyph is too tall, don't cache it
620    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
621        ALOGE("Font size to large to fit in cache. width, height = %i, %i",
622                (int) glyph.fWidth, (int) glyph.fHeight);
623        return;
624    }
625
626    // Now copy the bitmap into the cache texture
627    uint32_t startX = 0;
628    uint32_t startY = 0;
629
630    bool bitmapFit = false;
631    CacheTextureLine *cacheLine;
632    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
633        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
634        if (bitmapFit) {
635            cacheLine = mCacheLines[i];
636            break;
637        }
638    }
639
640    // If the new glyph didn't fit, flush the state so far and invalidate everything
641    if (!bitmapFit) {
642        flushAllAndInvalidate();
643
644        // Try to fit it again
645        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
646            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
647            if (bitmapFit) {
648                cacheLine = mCacheLines[i];
649                break;
650            }
651        }
652
653        // if we still don't fit, something is wrong and we shouldn't draw
654        if (!bitmapFit) {
655            return;
656        }
657    }
658
659    cachedGlyph->mCachedTextureLine = cacheLine;
660
661    *retOriginX = startX;
662    *retOriginY = startY;
663
664    uint32_t endX = startX + glyph.fWidth;
665    uint32_t endY = startY + glyph.fHeight;
666
667    uint32_t cacheWidth = cacheLine->mMaxWidth;
668
669    CacheTexture* cacheTexture = cacheLine->mCacheTexture;
670    if (!cacheTexture->mTexture) {
671        // Large-glyph texture memory is allocated only as needed
672        allocateTextureMemory(cacheTexture);
673    }
674
675    uint8_t* cacheBuffer = cacheTexture->mTexture;
676    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
677    unsigned int stride = glyph.rowBytes();
678
679    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
680    if (mGammaTable) {
681        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
682            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
683                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
684                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
685            }
686        }
687    } else {
688        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
689            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
690                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
691                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
692            }
693        }
694    }
695
696    cachedGlyph->mIsValid = true;
697}
698
699CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
700    uint8_t* textureMemory = NULL;
701    CacheTexture* cacheTexture = new CacheTexture(textureMemory, width, height);
702
703    if (allocate) {
704        allocateTextureMemory(cacheTexture);
705    }
706
707    return cacheTexture;
708}
709
710void FontRenderer::initTextTexture() {
711    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
712        delete mCacheLines[i];
713    }
714    mCacheLines.clear();
715
716    if (mCacheTextureSmall) {
717        delete mCacheTextureSmall;
718        delete mCacheTexture128;
719        delete mCacheTexture256;
720        delete mCacheTexture512;
721    }
722
723    // Next, use other, separate caches for large glyphs.
724    uint16_t maxWidth = 0;
725    if (Caches::hasInstance()) {
726        maxWidth = Caches::getInstance().maxTextureSize;
727    }
728
729    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
730        maxWidth = MAX_TEXT_CACHE_WIDTH;
731    }
732
733    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
734    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
735    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
736    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
737    mCurrentCacheTexture = mCacheTextureSmall;
738
739    mUploadTexture = false;
740    // Split up our default cache texture into lines of certain widths
741    int nextLine = 0;
742    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
743    nextLine += mCacheLines.top()->mMaxHeight;
744    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
745    nextLine += mCacheLines.top()->mMaxHeight;
746    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
747    nextLine += mCacheLines.top()->mMaxHeight;
748    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
749    nextLine += mCacheLines.top()->mMaxHeight;
750    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
751    nextLine += mCacheLines.top()->mMaxHeight;
752    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
753    nextLine += mCacheLines.top()->mMaxHeight;
754    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
755            nextLine, 0, mCacheTextureSmall));
756
757    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
758    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
759    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
760    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
761    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
762}
763
764// Avoid having to reallocate memory and render quad by quad
765void FontRenderer::initVertexArrayBuffers() {
766    uint32_t numIndices = mMaxNumberOfQuads * 6;
767    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
768    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
769
770    // Four verts, two triangles , six indices per quad
771    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
772        int i6 = i * 6;
773        int i4 = i * 4;
774
775        indexBufferData[i6 + 0] = i4 + 0;
776        indexBufferData[i6 + 1] = i4 + 1;
777        indexBufferData[i6 + 2] = i4 + 2;
778
779        indexBufferData[i6 + 3] = i4 + 0;
780        indexBufferData[i6 + 4] = i4 + 2;
781        indexBufferData[i6 + 5] = i4 + 3;
782    }
783
784    glGenBuffers(1, &mIndexBufferID);
785    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
786    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
787
788    free(indexBufferData);
789
790    uint32_t coordSize = 2;
791    uint32_t uvSize = 2;
792    uint32_t vertsPerQuad = 4;
793    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
794    mTextMeshPtr = new float[vertexBufferSize];
795}
796
797// We don't want to allocate anything unless we actually draw text
798void FontRenderer::checkInit() {
799    if (mInitialized) {
800        return;
801    }
802
803    initTextTexture();
804    initVertexArrayBuffers();
805
806    mInitialized = true;
807}
808
809void FontRenderer::checkTextureUpdate() {
810    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
811        return;
812    }
813
814    Caches& caches = Caches::getInstance();
815    GLuint lastTextureId = 0;
816    // Iterate over all the cache lines and see which ones need to be updated
817    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
818        CacheTextureLine* cl = mCacheLines[i];
819        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
820            CacheTexture* cacheTexture = cl->mCacheTexture;
821            uint32_t xOffset = 0;
822            uint32_t yOffset = cl->mCurrentRow;
823            uint32_t width   = cl->mMaxWidth;
824            uint32_t height  = cl->mMaxHeight;
825            void* textureData = cacheTexture->mTexture + (yOffset * width);
826
827            if (cacheTexture->mTextureId != lastTextureId) {
828                caches.activeTexture(0);
829                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
830                lastTextureId = cacheTexture->mTextureId;
831            }
832            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
833                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
834
835            cl->mDirty = false;
836        }
837    }
838
839    caches.activeTexture(0);
840    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
841    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
842        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
843        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
844        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
845        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
846    }
847    mLastCacheTexture = mCurrentCacheTexture;
848
849    mUploadTexture = false;
850}
851
852void FontRenderer::issueDrawCommand() {
853    checkTextureUpdate();
854
855    Caches& caches = Caches::getInstance();
856    caches.bindIndicesBuffer(mIndexBufferID);
857    if (!mDrawn) {
858        float* buffer = mTextMeshPtr;
859        int offset = 2;
860
861        bool force = caches.unbindMeshBuffer();
862        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
863        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
864                buffer + offset);
865    }
866
867    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
868
869    mDrawn = true;
870}
871
872void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
873        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
874        float x4, float y4, float u4, float v4, CacheTexture* texture) {
875    if (texture != mCurrentCacheTexture) {
876        if (mCurrentQuadIndex != 0) {
877            // First, draw everything stored already which uses the previous texture
878            issueDrawCommand();
879            mCurrentQuadIndex = 0;
880        }
881        // Now use the new texture id
882        mCurrentCacheTexture = texture;
883    }
884
885    const uint32_t vertsPerQuad = 4;
886    const uint32_t floatsPerVert = 4;
887    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
888
889    (*currentPos++) = x1;
890    (*currentPos++) = y1;
891    (*currentPos++) = u1;
892    (*currentPos++) = v1;
893
894    (*currentPos++) = x2;
895    (*currentPos++) = y2;
896    (*currentPos++) = u2;
897    (*currentPos++) = v2;
898
899    (*currentPos++) = x3;
900    (*currentPos++) = y3;
901    (*currentPos++) = u3;
902    (*currentPos++) = v3;
903
904    (*currentPos++) = x4;
905    (*currentPos++) = y4;
906    (*currentPos++) = u4;
907    (*currentPos++) = v4;
908
909    mCurrentQuadIndex++;
910}
911
912void FontRenderer::appendMeshQuad(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    if (mClip &&
917            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
918        return;
919    }
920
921    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
922
923    if (mBounds) {
924        mBounds->left = fmin(mBounds->left, x1);
925        mBounds->top = fmin(mBounds->top, y3);
926        mBounds->right = fmax(mBounds->right, x3);
927        mBounds->bottom = fmax(mBounds->bottom, y1);
928    }
929
930    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
931        issueDrawCommand();
932        mCurrentQuadIndex = 0;
933    }
934}
935
936void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
937        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
938        float x4, float y4, float u4, float v4, CacheTexture* texture) {
939
940    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
941
942    if (mBounds) {
943        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
944        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
945        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
946        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
947    }
948
949    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
950        issueDrawCommand();
951        mCurrentQuadIndex = 0;
952    }
953}
954
955uint32_t FontRenderer::getRemainingCacheCapacity() {
956    uint32_t remainingCapacity = 0;
957    float totalPixels = 0;
958    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
959         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
960         totalPixels += mCacheLines[i]->mMaxWidth;
961    }
962    remainingCapacity = (remainingCapacity * 100) / totalPixels;
963    return remainingCapacity;
964}
965
966void FontRenderer::precacheLatin(SkPaint* paint) {
967    // Remaining capacity is measured in %
968    uint32_t remainingCapacity = getRemainingCacheCapacity();
969    uint32_t precacheIndex = 0;
970
971    // We store a string with letters in a rough frequency of occurrence
972    String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
973
974    size_t size = l.size();
975    uint16_t latin[size];
976    paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
977
978    while (remainingCapacity > 25 && precacheIndex < size) {
979        mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
980        remainingCapacity = getRemainingCacheCapacity();
981        precacheIndex++;
982    }
983}
984
985void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
986    uint32_t currentNumFonts = mActiveFonts.size();
987    int flags = 0;
988    if (paint->isFakeBoldText()) {
989        flags |= Font::kFakeBold;
990    }
991
992    const float skewX = paint->getTextSkewX();
993    uint32_t italicStyle = *(uint32_t*) &skewX;
994    const float scaleXFloat = paint->getTextScaleX();
995    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
996    SkPaint::Style style = paint->getStyle();
997    const float strokeWidthFloat = paint->getStrokeWidth();
998    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
999    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1000            scaleX, style, strokeWidth);
1001
1002    const float maxPrecacheFontSize = 40.0f;
1003    bool isNewFont = currentNumFonts != mActiveFonts.size();
1004
1005    if (isNewFont && fontSize <= maxPrecacheFontSize) {
1006        precacheLatin(paint);
1007    }
1008}
1009
1010FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1011        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
1012    checkInit();
1013
1014    if (!mCurrentFont) {
1015        DropShadow image;
1016        image.width = 0;
1017        image.height = 0;
1018        image.image = NULL;
1019        image.penX = 0;
1020        image.penY = 0;
1021        return image;
1022    }
1023
1024    mDrawn = false;
1025    mClip = NULL;
1026    mBounds = NULL;
1027
1028    Rect bounds;
1029    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1030
1031    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1032    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1033    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1034
1035    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1036        dataBuffer[i] = 0;
1037    }
1038
1039    int penX = radius - bounds.left;
1040    int penY = radius - bounds.bottom;
1041
1042    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1043            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1044    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1045
1046    DropShadow image;
1047    image.width = paddedWidth;
1048    image.height = paddedHeight;
1049    image.image = dataBuffer;
1050    image.penX = penX;
1051    image.penY = penY;
1052
1053    return image;
1054}
1055
1056void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1057    checkInit();
1058
1059    mDrawn = false;
1060    mBounds = bounds;
1061    mClip = clip;
1062}
1063
1064void FontRenderer::finishRender() {
1065    mBounds = NULL;
1066    mClip = NULL;
1067
1068    if (mCurrentQuadIndex != 0) {
1069        issueDrawCommand();
1070        mCurrentQuadIndex = 0;
1071    }
1072}
1073
1074bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1075        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1076    if (!mCurrentFont) {
1077        ALOGE("No font set");
1078        return false;
1079    }
1080
1081    initRender(clip, bounds);
1082    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1083    finishRender();
1084
1085    return mDrawn;
1086}
1087
1088bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1089        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1090        const float* positions, Rect* bounds) {
1091    if (!mCurrentFont) {
1092        ALOGE("No font set");
1093        return false;
1094    }
1095
1096    initRender(clip, bounds);
1097    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1098    finishRender();
1099
1100    return mDrawn;
1101}
1102
1103bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1104        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1105        float hOffset, float vOffset, Rect* bounds) {
1106    if (!mCurrentFont) {
1107        ALOGE("No font set");
1108        return false;
1109    }
1110
1111    initRender(clip, bounds);
1112    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1113    finishRender();
1114
1115    return mDrawn;
1116}
1117
1118void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1119    // Compute gaussian weights for the blur
1120    // e is the euler's number
1121    float e = 2.718281828459045f;
1122    float pi = 3.1415926535897932f;
1123    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1124    // x is of the form [-radius .. 0 .. radius]
1125    // and sigma varies with radius.
1126    // Based on some experimental radius values and sigma's
1127    // we approximately fit sigma = f(radius) as
1128    // sigma = radius * 0.3  + 0.6
1129    // The larger the radius gets, the more our gaussian blur
1130    // will resemble a box blur since with large sigma
1131    // the gaussian curve begins to lose its shape
1132    float sigma = 0.3f * (float) radius + 0.6f;
1133
1134    // Now compute the coefficints
1135    // We will store some redundant values to save some math during
1136    // the blur calculations
1137    // precompute some values
1138    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1139    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1140
1141    float normalizeFactor = 0.0f;
1142    for (int32_t r = -radius; r <= radius; r ++) {
1143        float floatR = (float) r;
1144        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1145        normalizeFactor += weights[r + radius];
1146    }
1147
1148    //Now we need to normalize the weights because all our coefficients need to add up to one
1149    normalizeFactor = 1.0f / normalizeFactor;
1150    for (int32_t r = -radius; r <= radius; r ++) {
1151        weights[r + radius] *= normalizeFactor;
1152    }
1153}
1154
1155void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1156        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1157    float blurredPixel = 0.0f;
1158    float currentPixel = 0.0f;
1159
1160    for (int32_t y = 0; y < height; y ++) {
1161
1162        const uint8_t* input = source + y * width;
1163        uint8_t* output = dest + y * width;
1164
1165        for (int32_t x = 0; x < width; x ++) {
1166            blurredPixel = 0.0f;
1167            const float* gPtr = weights;
1168            // Optimization for non-border pixels
1169            if (x > radius && x < (width - radius)) {
1170                const uint8_t *i = input + (x - radius);
1171                for (int r = -radius; r <= radius; r ++) {
1172                    currentPixel = (float) (*i);
1173                    blurredPixel += currentPixel * gPtr[0];
1174                    gPtr++;
1175                    i++;
1176                }
1177            } else {
1178                for (int32_t r = -radius; r <= radius; r ++) {
1179                    // Stepping left and right away from the pixel
1180                    int validW = x + r;
1181                    if (validW < 0) {
1182                        validW = 0;
1183                    }
1184                    if (validW > width - 1) {
1185                        validW = width - 1;
1186                    }
1187
1188                    currentPixel = (float) input[validW];
1189                    blurredPixel += currentPixel * gPtr[0];
1190                    gPtr++;
1191                }
1192            }
1193            *output = (uint8_t)blurredPixel;
1194            output ++;
1195        }
1196    }
1197}
1198
1199void FontRenderer::verticalBlur(float* weights, int32_t radius,
1200        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1201    float blurredPixel = 0.0f;
1202    float currentPixel = 0.0f;
1203
1204    for (int32_t y = 0; y < height; y ++) {
1205
1206        uint8_t* output = dest + y * width;
1207
1208        for (int32_t x = 0; x < width; x ++) {
1209            blurredPixel = 0.0f;
1210            const float* gPtr = weights;
1211            const uint8_t* input = source + x;
1212            // Optimization for non-border pixels
1213            if (y > radius && y < (height - radius)) {
1214                const uint8_t *i = input + ((y - radius) * width);
1215                for (int32_t r = -radius; r <= radius; r ++) {
1216                    currentPixel = (float)(*i);
1217                    blurredPixel += currentPixel * gPtr[0];
1218                    gPtr++;
1219                    i += width;
1220                }
1221            } else {
1222                for (int32_t r = -radius; r <= radius; r ++) {
1223                    int validH = y + r;
1224                    // Clamp to zero and width
1225                    if (validH < 0) {
1226                        validH = 0;
1227                    }
1228                    if (validH > height - 1) {
1229                        validH = height - 1;
1230                    }
1231
1232                    const uint8_t *i = input + validH * width;
1233                    currentPixel = (float) (*i);
1234                    blurredPixel += currentPixel * gPtr[0];
1235                    gPtr++;
1236                }
1237            }
1238            *output = (uint8_t) blurredPixel;
1239            output ++;
1240        }
1241    }
1242}
1243
1244
1245void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1246    float *gaussian = new float[2 * radius + 1];
1247    computeGaussianWeights(gaussian, radius);
1248
1249    uint8_t* scratch = new uint8_t[width * height];
1250
1251    horizontalBlur(gaussian, radius, image, scratch, width, height);
1252    verticalBlur(gaussian, radius, scratch, image, width, height);
1253
1254    delete[] gaussian;
1255    delete[] scratch;
1256}
1257
1258}; // namespace uirenderer
1259}; // namespace android
1260