FontRenderer.cpp revision 514fb18827186591d66973c2362c859b64b63556
1694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy/*
2694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * Copyright (C) 2010 The Android Open Source Project
3694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy *
4694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * Licensed under the Apache License, Version 2.0 (the "License");
5694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * you may not use this file except in compliance with the License.
6694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * You may obtain a copy of the License at
7694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy *
8694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy *      http://www.apache.org/licenses/LICENSE-2.0
9694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy *
10694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * Unless required by applicable law or agreed to in writing, software
11694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * distributed under the License is distributed on an "AS IS" BASIS,
12694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * See the License for the specific language governing permissions and
14694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy * limitations under the License.
15694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy */
16694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
17694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy#define LOG_TAG "OpenGLRenderer"
18694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
19694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy#include <SkUtils.h>
20694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include <cutils/properties.h>
22e2d345ea67e2960b37bfdc0fc8626d1bfa747404Romain Guy
2351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include <utils/Log.h>
2451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
2551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include "FontRenderer.h"
2651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
27694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace android {
28694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace uirenderer {
29694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
30694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
3151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy// Defines
3251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
3351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_WIDTH 1024
3551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_HEIGHT 256
3651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
38694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Font
39694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
40694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
412577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont::Font(FontRenderer* state, uint32_t fontId, float fontSize,
422577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        int flags, uint32_t italicStyle) :
432577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mState(state), mFontId(fontId), mFontSize(fontSize),
442577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mFlags(flags), mItalicStyle(italicStyle) {
45694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
46694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
47694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
48694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::~Font() {
49694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
50694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (mState->mActiveFonts[ct] == this) {
51694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            mState->mActiveFonts.removeAt(ct);
52694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
53694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
54694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
55694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
56694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
5751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
58694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete glyph;
59694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
60694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
61694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
62694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::invalidateTextureCache() {
63694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
64694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCachedGlyphs.valueAt(i)->mIsValid = false;
65694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
66694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
67694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
68f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
69f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
70f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
71f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
72f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int width = (int) glyph->mBitmapWidth;
73f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int height = (int) glyph->mBitmapHeight;
74f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
7561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->bottom > nPenY) {
76f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->bottom = nPenY;
77f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
7861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->left > nPenX) {
79f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->left = nPenX;
80f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->right < nPenX + width) {
82f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->right = nPenX + width;
83f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->top < nPenY + height) {
85f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->top = nPenY + height;
86f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
87f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
88f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
89694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
90694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenX = x + glyph->mBitmapLeft;
91694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
92694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
9351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u1 = glyph->mBitmapMinU;
9451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u2 = glyph->mBitmapMaxU;
9551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v1 = glyph->mBitmapMinV;
9651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v2 = glyph->mBitmapMaxV;
9751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
9851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int width = (int) glyph->mBitmapWidth;
9951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int height = (int) glyph->mBitmapHeight;
10051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
10151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
10251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY, 0, u2, v2,
10351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY - height, 0, u2, v1,
10451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX, nPenY - height, 0, u1, v1);
105694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
106694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
107b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
108b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
10989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
11089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
11189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
11389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
11489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t cacheWidth = mState->getCacheWidth();
11689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    const uint8_t* cacheBuffer = mState->getTextTextureData();
11789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
118f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint32_t cacheX = 0, cacheY = 0;
119f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int32_t bX = 0, bY = 0;
12089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
12189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
122b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
123f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                LOGE("Skipping invalid index");
124f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                continue;
125f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            }
12689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
12789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            bitmap[bY * bitmapW + bX] = tempCol;
12889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
12989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
13089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
13289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13365ef909776c03417d8b597738da54ca211e37e4fAlex SakhartchoukFont::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
1341e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    CachedGlyphInfo* cachedGlyph = NULL;
1351e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
1361e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (index >= 0) {
1371e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        cachedGlyph = mCachedGlyphs.valueAt(index);
1381e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    } else {
13965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        cachedGlyph = cacheGlyph(paint, utfChar);
14065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
14165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Is the glyph still in texture cache?
14365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if (!cachedGlyph->mIsValid) {
14465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
14565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
14665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
14765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return cachedGlyph;
14965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
15065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
15151769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
15261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
15361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
15461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
15561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy                bitmapW, bitmapH, NULL);
15661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    } else {
15761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
158f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
159f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
160f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
161f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
162f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
16361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, Rect *bounds) {
16461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds == NULL) {
165f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        LOGE("No return rectangle provided to measure text");
166f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        return;
167f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
168f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    bounds->set(1e6, -1e6, -1e6, 1e6);
169f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
170f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
171f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
17258ef7fbf16864164efe98bf613b15c64deb1afc0Romain Guy#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
1732bffd268f135df8308c9e67af110525a5c463424Romain Guy
174f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
17561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
17661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
177694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
178694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
179694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
180694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1812bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed penX = SkIntToFixed(x);
1822bffd268f135df8308c9e67af110525a5c463424Romain Guy    int penY = y;
183694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int glyphsLeft = 1;
184694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs > 0) {
185694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        glyphsLeft = numGlyphs;
186694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
187694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1882bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed prevRsbDelta = 0;
1892bffd268f135df8308c9e67af110525a5c463424Romain Guy    penX += SK_Fixed1 / 2;
1902bffd268f135df8308c9e67af110525a5c463424Romain Guy
191694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    text += start;
192694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
193694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    while (glyphsLeft > 0) {
194694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
195694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
19661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        // Reached the end of the string
197694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (utfChar < 0) {
198694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
199694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
200694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
20165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
2022bffd268f135df8308c9e67af110525a5c463424Romain Guy        penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
2032bffd268f135df8308c9e67af110525a5c463424Romain Guy        prevRsbDelta = cachedGlyph->mRsbDelta;
204694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
205694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
206694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (cachedGlyph->mIsValid) {
207f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            switch(mode) {
208f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case FRAMEBUFFER:
2092bffd268f135df8308c9e67af110525a5c463424Romain Guy                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
210f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
211f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case BITMAP:
2122bffd268f135df8308c9e67af110525a5c463424Romain Guy                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
213f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
214f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case MEASURE:
2152bffd268f135df8308c9e67af110525a5c463424Romain Guy                measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
216f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
21789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
218694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
219694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2202bffd268f135df8308c9e67af110525a5c463424Romain Guy        penX += cachedGlyph->mAdvanceX;
221694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
222694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If we were given a specific number of glyphs, decrement
223694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (numGlyphs > 0) {
224694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            glyphsLeft--;
225694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
226694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
227694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
228694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
22951769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
230694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
231694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
232694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapLeft = skiaGlyph.fLeft;
233694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapTop = skiaGlyph.fTop;
2342bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
2352bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
236694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
237694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
238694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
239694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
240694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Get the bitmap for the glyph
241694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    paint->findImage(skiaGlyph);
24251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
243694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
244694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!glyph->mIsValid) {
245694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
246694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
247694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
248694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + skiaGlyph.fWidth;
249694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + skiaGlyph.fHeight;
250694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartX = startX;
25289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartY = startY;
253694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapWidth = skiaGlyph.fWidth;
254694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapHeight = skiaGlyph.fHeight;
255694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheWidth = mState->getCacheWidth();
25751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheHeight = mState->getCacheHeight();
258694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
259694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
260694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
261694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
262694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
263694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
26451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->mUploadTexture = true;
265694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
266694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
26751769a68a5cb34e9564740c6a854fcb93018789dRomain GuyFont::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
26851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
269694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCachedGlyphs.add(glyph, newGlyph);
270694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
271694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
272694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mGlyphIndex = skiaGlyph.fID;
273694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mIsValid = false;
274694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
275694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    updateGlyphCache(paint, skiaGlyph, newGlyph);
276694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
277694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newGlyph;
278694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
279694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2802577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
2812577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        int flags, uint32_t italicStyle) {
282694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> &activeFonts = state->mActiveFonts;
283694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
284694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < activeFonts.size(); i++) {
28551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        Font* font = activeFonts[i];
2862577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        if (font->mFontId == fontId && font->mFontSize == fontSize &&
2872577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy                font->mFlags == flags && font->mItalicStyle == italicStyle) {
28851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
289694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
290694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2922577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
293694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
294694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
295694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
296694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
297694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
298694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
299694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
300694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
301514fb18827186591d66973c2362c859b64b63556Romain Guystatic bool sLogFontRendererCreate = true;
302514fb18827186591d66973c2362c859b64b63556Romain Guy
303694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
304514fb18827186591d66973c2362c859b64b63556Romain Guy    if (sLogFontRendererCreate) LOGD("Creating FontRenderer");
30551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
306b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
307694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
308694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
309694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
3109b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextureId = 0;
311694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3129cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
3139cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextTexture = NULL;
3149cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
315694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
316694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
31751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
3189b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
31951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
32051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
32151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
322514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) LOGD("  Setting text cache width to %s pixels", property);
32351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheWidth = atoi(property);
32451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
325514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
326514fb18827186591d66973c2362c859b64b63556Romain Guy            LOGD("  Using default text cache width of %i pixels", mCacheWidth);
327514fb18827186591d66973c2362c859b64b63556Romain Guy        }
32851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
32951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
33051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
331514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) LOGD("  Setting text cache width to %s pixels", property);
33251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheHeight = atoi(property);
33351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
334514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
335514fb18827186591d66973c2362c859b64b63556Romain Guy            LOGD("  Using default text cache height of %i pixels", mCacheHeight);
336514fb18827186591d66973c2362c859b64b63556Romain Guy        }
33751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
338514fb18827186591d66973c2362c859b64b63556Romain Guy
339514fb18827186591d66973c2362c859b64b63556Romain Guy    sLogFontRendererCreate = false;
340694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
341694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
342694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
343694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
344694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete mCacheLines[i];
345694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
346694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCacheLines.clear();
347694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3489cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
3499cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
3509cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextTexture;
3519cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    }
3529b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3539cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mTextureId) {
3549b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        glDeleteTextures(1, &mTextureId);
3559b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
356694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
357694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
358694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
359694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
360694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
361694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
362694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
363694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
364694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
365694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
366694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
367694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
368694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
369694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
370694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
371694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
372694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCacheLines[i]->mCurrentCol = 0;
373694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
374694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
375694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
37651769a68a5cb34e9564740c6a854fcb93018789dRomain Guybool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
377694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
3782bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
379694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        LOGE("Font size to large to fit in cache. width, height = %i, %i",
380694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                (int) glyph.fWidth, (int) glyph.fHeight);
381694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return false;
382694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
383694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
385694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
386694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
387694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
388694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    bool bitmapFit = false;
389694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
390694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
391694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (bitmapFit) {
392694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
393694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
394694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
395694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
396694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
397694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!bitmapFit) {
398694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
400694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
401694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            if (bitmapFit) {
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                break;
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            }
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
407694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // if we still don't fit, something is wrong and we shouldn't draw
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (!bitmapFit) {
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                    (int) glyph.fWidth, (int) glyph.fHeight);
412694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            return false;
413694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
416694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
417694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
418694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
419694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
420694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
421694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
422694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheWidth = mCacheWidth;
423694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
42489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* cacheBuffer = mTextTexture;
42589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
426694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
427694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
428694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
429694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
430694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
43189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
432b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
433694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
434694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
435694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
436694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return true;
437694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
438694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
439694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initTextTexture() {
44089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
4411de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
4421de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy
443694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mUploadTexture = false;
444694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenTextures(1, &mTextureId);
446694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindTexture(GL_TEXTURE_2D, mTextureId);
447694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
4489b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Initialize texture dimentions
4499b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
45061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
451694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
452e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    mLinearFiltering = false;
453e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
454e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
455694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
456694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
457694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
458694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
459694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Split up our cache texture into lines of certain widths
460694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nextLine = 0;
4617975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
462694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4637975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
464694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4657975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
46665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    nextLine += mCacheLines.top()->mMaxHeight;
4677975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
468694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4697975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
470694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4717975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
472694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
47351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
474694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
475694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
476694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
477694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
478694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t numIndicies = mMaxNumberOfQuads * 6;
479694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
48051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
481694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
482694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
483694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
484694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
485694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
486694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
487694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
488694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
489694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
490694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
491694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
492694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
493694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
494694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
495694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
496694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
4975d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
4985d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
4995d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
500694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
501694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
502694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
503694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t coordSize = 3;
504694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
505694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
5069b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
5079b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
508694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
509694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
510694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
511694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
512694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
513694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
514694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
515694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
516694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
517694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
518694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
51965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // We store a string with letters in a rough frequency of occurrence
52065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
52165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
52265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16(",.?!()-+@;:`'");
52365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("0123456789");
52465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
525694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
526694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
527694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5289b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
5299b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    if (!mUploadTexture) {
5309b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
5319b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
5329b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5339b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glBindTexture(GL_TEXTURE_2D, mTextureId);
5349b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5359b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Iterate over all the cache lines and see which ones need to be updated
5369b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
5379b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        CacheTextureLine* cl = mCacheLines[i];
5389b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        if(cl->mDirty) {
5399b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
5409b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t yOffset = cl->mCurrentRow;
5419b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t width   = mCacheWidth;
5429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t height  = cl->mMaxHeight;
5431e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            void* textureData = mTextTexture + yOffset*width;
5449b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5459b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
5461e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
5479b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5489b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            cl->mDirty = false;
5499b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
550694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
551694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5529b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
5539b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
5549b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5559b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
5569b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
5579b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
55851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* vtx = mTextMeshPtr;
55951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* tex = vtx + 3;
560694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
561694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // position is slot 0
562694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t slot = 0;
563694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
564694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
565694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // texture0 is slot 1
566694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    slot = 1;
567694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
568694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
569694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
570694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
5715b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
5725b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
574694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
576694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
577694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float x4, float y4, float z4, float u4, float v4) {
57809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
57909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        return;
58009147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    }
58109147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
583694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t floatsPerVert = 5;
58451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
585694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
586694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
587694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
588694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z1;
589694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
590694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
591694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
592694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
593694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
594694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z2;
595694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
596694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
597694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
598694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
599694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
600694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z3;
601694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
602694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
603694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
604694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
605694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
606694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z4;
607694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
608694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
609694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
610694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
611694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6125b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
6135b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
6145b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
6155b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
6165b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
6175b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
6185b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
619694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
620694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
621694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
622694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
623694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
624694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
62565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukuint32_t FontRenderer::getRemainingCacheCapacity() {
62665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = 0;
62765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    float totalPixels = 0;
62865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
62965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
63065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         totalPixels += mCacheLines[i]->mMaxWidth;
63165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
63265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    remainingCapacity = (remainingCapacity * 100) / totalPixels;
63365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return remainingCapacity;
63465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
63565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
63665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::precacheLatin(SkPaint* paint) {
63765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Remaining capacity is measured in %
63865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = getRemainingCacheCapacity();
63965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t precacheIdx = 0;
640054dc1840941665e32036f9523df51720ad069c8Romain Guy    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
641054dc1840941665e32036f9523df51720ad069c8Romain Guy        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
64265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        remainingCapacity = getRemainingCacheCapacity();
64365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheIdx ++;
64465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
64565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
64665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
64765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
64865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t currentNumFonts = mActiveFonts.size();
649325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    int flags = 0;
650325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    if (paint->isFakeBoldText()) {
651325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        flags |= Font::kFakeBold;
652325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    }
6532577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy
6542577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    const float skewX = paint->getTextSkewX();
6552577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    uint32_t italicStyle = *(uint32_t*) &skewX;
6562577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
65765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
65865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    const float maxPrecacheFontSize = 40.0f;
65965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    bool isNewFont = currentNumFonts != mActiveFonts.size();
66065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
6612bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (isNewFont && fontSize <= maxPrecacheFontSize) {
66265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheLatin(paint);
66365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
664694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
6657975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy
666f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
6671e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
6681e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
6691e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
6701e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
6711e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
6721e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
6731e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
6741e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
6751e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
6761e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
6771e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
6781e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
679f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
680f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
681f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
6821e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
6831e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
684f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
6851e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
686f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
687f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
6881e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
689f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
690f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
691f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
692f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
6931e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            dataBuffer, paddedWidth, paddedHeight);
694f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
695f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
696f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
697f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
698f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
699f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
700f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
701f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
702f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
703f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
704694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
7055b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guybool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
7065b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
707694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
708694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
70909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (!mCurrentFont) {
71009147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        LOGE("No font set");
7115b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        return false;
712694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
713694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
7145b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
7155b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
71609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
71751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
7185b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
719694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
720694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
721694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
722694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
723694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
7245b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
7255b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
726694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
727694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
72889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
72989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
73089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
73189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
73289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
73389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
73489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
73589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
73689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
73789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
738f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
73989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
74089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
74189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
742325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    float sigma = 0.3f * (float) radius + 0.6f;
74389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
74489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
74589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
74689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
74789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
74889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
74989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
75089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
752325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
7537975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
75489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
75589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
75689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
75789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
75989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
760325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
76189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
76289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
76389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
76489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
7661e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
76789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
76889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
76989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
770325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
77189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
77289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
77389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
77489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
775325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
77689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
77789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
77889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
779325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (x > radius && x < (width - radius)) {
78089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
781325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int r = -radius; r <= radius; r ++) {
7827975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
78389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
78489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
78589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
78689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
78789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
788325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
78989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
79089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
791325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW < 0) {
79289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
79389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
794325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW > width - 1) {
79589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
79689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
79789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
798325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) input[validW];
79989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
80089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
80189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
80289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
80389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
80489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
80589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
80689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
80789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
80889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
80989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
8101e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
81189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
81289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
81389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
814325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
81589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
81689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
81789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
818325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
81989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
82089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
82189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
82289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
823325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (y > radius && y < (height - radius)) {
82489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
825325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
82689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
82789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
82889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
82989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
83089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
83189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
832325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
83389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
83489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
835325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH < 0) {
83689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
83789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
838325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH > height - 1) {
83989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
84089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
84189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
84289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
843325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) (*i);
84489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
84589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
84689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
84789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
848325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            *output = (uint8_t) blurredPixel;
84989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
85089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
85189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
85289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
85389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
85489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
85589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
85689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
85789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
85889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
85989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
86089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
86189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
86289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
86389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
86489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
865694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
866694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
867