FontRenderer.cpp revision 5b3b35296e8b2c8d3f07d32bb645d5414db41a1d
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
41694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
42694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mState(state), mFontId(fontId), mFontSize(fontSize) {
43694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
44694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
45694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
46694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::~Font() {
47694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
48694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (mState->mActiveFonts[ct] == this) {
49694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            mState->mActiveFonts.removeAt(ct);
50694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
51694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
52694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
53694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
54694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
5551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
56694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete glyph;
57694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
58694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
59694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
60694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::invalidateTextureCache() {
61694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
62694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCachedGlyphs.valueAt(i)->mIsValid = false;
63694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
64694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
65694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
66f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
67f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
68f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
69f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
70f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int width = (int) glyph->mBitmapWidth;
71f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int height = (int) glyph->mBitmapHeight;
72f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
7361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->bottom > nPenY) {
74f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->bottom = nPenY;
75f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
7661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->left > nPenX) {
77f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->left = nPenX;
78f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
7961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->right < nPenX + width) {
80f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->right = nPenX + width;
81f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->top < nPenY + height) {
83f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->top = nPenY + height;
84f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
85f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
86f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
87694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
88694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenX = x + glyph->mBitmapLeft;
89694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
90694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
9151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u1 = glyph->mBitmapMinU;
9251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u2 = glyph->mBitmapMaxU;
9351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v1 = glyph->mBitmapMinV;
9451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v2 = glyph->mBitmapMaxV;
9551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
9651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int width = (int) glyph->mBitmapWidth;
9751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int height = (int) glyph->mBitmapHeight;
9851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
9951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
10051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY, 0, u2, v2,
10151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY - height, 0, u2, v1,
10251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX, nPenY - height, 0, u1, v1);
103694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
104694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
105b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
106b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
10789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
10889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
10989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
11189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
11289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t cacheWidth = mState->getCacheWidth();
11489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    const uint8_t* cacheBuffer = mState->getTextTextureData();
11589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
116f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint32_t cacheX = 0, cacheY = 0;
117f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int32_t bX = 0, bY = 0;
11889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
11989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
120b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
121f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                LOGE("Skipping invalid index");
122f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                continue;
123f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            }
12489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
12589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            bitmap[bY * bitmapW + bX] = tempCol;
12689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
12789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
12889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
12989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
13089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13165ef909776c03417d8b597738da54ca211e37e4fAlex SakhartchoukFont::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
1321e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    CachedGlyphInfo* cachedGlyph = NULL;
1331e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
1341e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (index >= 0) {
1351e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        cachedGlyph = mCachedGlyphs.valueAt(index);
1361e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    } else {
13765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        cachedGlyph = cacheGlyph(paint, utfChar);
13865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
13965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Is the glyph still in texture cache?
14165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if (!cachedGlyph->mIsValid) {
14265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
14365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
14465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
14565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return cachedGlyph;
14765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
14865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14951769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
15061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
15161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
15261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
15361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy                bitmapW, bitmapH, NULL);
15461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    } else {
15561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
156f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
157f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
158f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
159f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
160f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
16161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, Rect *bounds) {
16261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds == NULL) {
163f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        LOGE("No return rectangle provided to measure text");
164f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        return;
165f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
166f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    bounds->set(1e6, -1e6, -1e6, 1e6);
167f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
168f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
169f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
17058ef7fbf16864164efe98bf613b15c64deb1afc0Romain Guy#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
1712bffd268f135df8308c9e67af110525a5c463424Romain Guy
172f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
17361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
17461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
175694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
176694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
177694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
178694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1792bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed penX = SkIntToFixed(x);
1802bffd268f135df8308c9e67af110525a5c463424Romain Guy    int penY = y;
181694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int glyphsLeft = 1;
182694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs > 0) {
183694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        glyphsLeft = numGlyphs;
184694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
185694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1862bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed prevRsbDelta = 0;
1872bffd268f135df8308c9e67af110525a5c463424Romain Guy    penX += SK_Fixed1 / 2;
1882bffd268f135df8308c9e67af110525a5c463424Romain Guy
189694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    text += start;
190694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
191694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    while (glyphsLeft > 0) {
192694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
193694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
19461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        // Reached the end of the string
195694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (utfChar < 0) {
196694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
197694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
198694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
19965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
2002bffd268f135df8308c9e67af110525a5c463424Romain Guy        penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
2012bffd268f135df8308c9e67af110525a5c463424Romain Guy        prevRsbDelta = cachedGlyph->mRsbDelta;
202694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
203694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
204694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (cachedGlyph->mIsValid) {
205f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            switch(mode) {
206f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case FRAMEBUFFER:
2072bffd268f135df8308c9e67af110525a5c463424Romain Guy                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
208f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
209f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case BITMAP:
2102bffd268f135df8308c9e67af110525a5c463424Romain Guy                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
211f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
212f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case MEASURE:
2132bffd268f135df8308c9e67af110525a5c463424Romain Guy                measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
214f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
21589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
216694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
217694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2182bffd268f135df8308c9e67af110525a5c463424Romain Guy        penX += cachedGlyph->mAdvanceX;
219694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
220694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If we were given a specific number of glyphs, decrement
221694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (numGlyphs > 0) {
222694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            glyphsLeft--;
223694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
224694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
225694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
226694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
22751769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
228694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
229694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
230694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapLeft = skiaGlyph.fLeft;
231694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapTop = skiaGlyph.fTop;
2322bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
2332bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
234694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
235694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
236694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
237694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
238694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Get the bitmap for the glyph
239694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    paint->findImage(skiaGlyph);
24051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
241694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
242694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!glyph->mIsValid) {
243694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
244694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
245694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
246694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + skiaGlyph.fWidth;
247694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + skiaGlyph.fHeight;
248694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
24989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartX = startX;
25089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartY = startY;
251694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapWidth = skiaGlyph.fWidth;
252694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapHeight = skiaGlyph.fHeight;
253694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheWidth = mState->getCacheWidth();
25551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheHeight = mState->getCacheHeight();
256694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
257694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
258694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
259694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
260694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
261694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
26251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->mUploadTexture = true;
263694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
264694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
26551769a68a5cb34e9564740c6a854fcb93018789dRomain GuyFont::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
26651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
267694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCachedGlyphs.add(glyph, newGlyph);
268694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
269694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
270694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mGlyphIndex = skiaGlyph.fID;
271694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mIsValid = false;
272694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
273694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    updateGlyphCache(paint, skiaGlyph, newGlyph);
274694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
275694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newGlyph;
276694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
277694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
278694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
279694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> &activeFonts = state->mActiveFonts;
280694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
281694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < activeFonts.size(); i++) {
28251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        Font* font = activeFonts[i];
28351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        if (font->mFontId == fontId && font->mFontSize == fontSize) {
28451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
285694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
286694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
287694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
288694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Font* newFont = new Font(state, fontId, fontSize);
289694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
290694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
292694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
293694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
294694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
295694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
296694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
297694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
29851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    LOGD("Creating FontRenderer");
29951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
300b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
301694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
302694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
303694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
3049b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextureId = 0;
305694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3069cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
3079cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextTexture = NULL;
3089cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
309694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
310694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
31151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
3129b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
31351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
31451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
31551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
31651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Setting text cache width to %s pixels", property);
31751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheWidth = atoi(property);
31851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
31951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
32051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
32151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
32251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
32351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Setting text cache width to %s pixels", property);
32451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheHeight = atoi(property);
32551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
32651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
32751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
328694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
329694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
330694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
331694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
332694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete mCacheLines[i];
333694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
334694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCacheLines.clear();
335694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3369cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
3379cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
3389cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextTexture;
3399cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    }
3409b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3419cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mTextureId) {
3429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        glDeleteTextures(1, &mTextureId);
3439b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
344694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
345694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
346694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
347694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
348694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
349694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
350694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
351694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
352694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
353694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
354694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
355694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
356694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
357694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
358694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
359694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
360694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCacheLines[i]->mCurrentCol = 0;
361694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
362694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
363694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
36451769a68a5cb34e9564740c6a854fcb93018789dRomain Guybool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
365694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
3662bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
367694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        LOGE("Font size to large to fit in cache. width, height = %i, %i",
368694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                (int) glyph.fWidth, (int) glyph.fHeight);
369694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return false;
370694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
371694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
372694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
373694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
374694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
375694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
376694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    bool bitmapFit = false;
377694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
378694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
379694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (bitmapFit) {
380694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
381694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
382694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
383694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
385694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!bitmapFit) {
386694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
387694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
388694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
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 we still don't fit, something is wrong and we shouldn't draw
397694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (!bitmapFit) {
398694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                    (int) glyph.fWidth, (int) glyph.fHeight);
400694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            return false;
401694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
407694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheWidth = mCacheWidth;
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
41289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* cacheBuffer = mTextTexture;
41389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
416694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
417694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
418694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
41989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
420b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
421694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
422694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
423694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
424694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return true;
425694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
426694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
427694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initTextTexture() {
42889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
4291de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
4301de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy
431694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mUploadTexture = false;
432694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
433694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenTextures(1, &mTextureId);
434694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindTexture(GL_TEXTURE_2D, mTextureId);
435694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
4369b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Initialize texture dimentions
4379b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
43861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
439694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
440e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    mLinearFiltering = false;
441e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
442e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
443694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
444694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
446694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
447694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Split up our cache texture into lines of certain widths
448694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nextLine = 0;
4497975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
450694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4517975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
452694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4537975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
45465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    nextLine += mCacheLines.top()->mMaxHeight;
4557975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
456694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4577975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
458694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4597975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
460694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
46151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
462694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
463694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
464694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
465694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
466694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t numIndicies = mMaxNumberOfQuads * 6;
467694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
46851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
469694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
470694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
471694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
472694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
473694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
474694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
475694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
476694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
477694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
478694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
479694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
480694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
481694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
482694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
483694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
484694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
4855d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
4865d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
4875d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
488694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
489694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
490694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
491694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t coordSize = 3;
492694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
493694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
4949b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
4959b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
496694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
497694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
498694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
499694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
500694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
501694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
502694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
503694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
504694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
505694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
506694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
50765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // We store a string with letters in a rough frequency of occurrence
50865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
50965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
51065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16(",.?!()-+@;:`'");
51165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("0123456789");
51265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
513694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
514694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
515694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5169b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
5179b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    if (!mUploadTexture) {
5189b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
5199b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
5209b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5219b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glBindTexture(GL_TEXTURE_2D, mTextureId);
5229b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5239b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Iterate over all the cache lines and see which ones need to be updated
5249b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
5259b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        CacheTextureLine* cl = mCacheLines[i];
5269b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        if(cl->mDirty) {
5279b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
5289b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t yOffset = cl->mCurrentRow;
5299b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t width   = mCacheWidth;
5309b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t height  = cl->mMaxHeight;
5311e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            void* textureData = mTextTexture + yOffset*width;
5329b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5339b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
5341e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
5359b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5369b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            cl->mDirty = false;
5379b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
538694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
539694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5409b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
5419b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
5429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5439b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
5449b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
5459b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
54651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* vtx = mTextMeshPtr;
54751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* tex = vtx + 3;
548694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
549694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // position is slot 0
550694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t slot = 0;
551694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
552694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
553694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // texture0 is slot 1
554694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    slot = 1;
555694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
556694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
557694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
558694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
5595b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
5605b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
561694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
562694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
563694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
564694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
565694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float x4, float y4, float z4, float u4, float v4) {
56609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
56709147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        return;
56809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    }
56909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
570694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
571694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t floatsPerVert = 5;
57251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
574694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
576694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z1;
577694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
578694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
579694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
580694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
581694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z2;
583694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
584694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
585694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
586694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
587694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
588694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z3;
589694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
590694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
591694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
592694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
593694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
594694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z4;
595694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
596694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
597694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
598694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
599694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6005b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
6015b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
6025b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
6035b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
6045b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
6055b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
6065b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
607694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
608694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
609694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
610694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
611694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
612694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
61365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukuint32_t FontRenderer::getRemainingCacheCapacity() {
61465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = 0;
61565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    float totalPixels = 0;
61665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
61765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
61865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         totalPixels += mCacheLines[i]->mMaxWidth;
61965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
62065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    remainingCapacity = (remainingCapacity * 100) / totalPixels;
62165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return remainingCapacity;
62265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
62365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
62465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::precacheLatin(SkPaint* paint) {
62565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Remaining capacity is measured in %
62665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = getRemainingCacheCapacity();
62765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t precacheIdx = 0;
628054dc1840941665e32036f9523df51720ad069c8Romain Guy    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
629054dc1840941665e32036f9523df51720ad069c8Romain Guy        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
63065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        remainingCapacity = getRemainingCacheCapacity();
63165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheIdx ++;
63265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
63365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
63465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
63565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
63665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t currentNumFonts = mActiveFonts.size();
637694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize);
63865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
63965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    const float maxPrecacheFontSize = 40.0f;
64065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    bool isNewFont = currentNumFonts != mActiveFonts.size();
64165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
6422bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (isNewFont && fontSize <= maxPrecacheFontSize) {
64365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheLatin(paint);
64465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
645694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
6467975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy
647f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
6481e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
6491e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
6501e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
6511e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
6521e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
6531e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
6541e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
6551e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
6561e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
6571e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
6581e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
6591e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
660f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
661f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
662f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
6631e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
6641e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
665f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
6661e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
667f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
668f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
6691e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
670f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
671f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
672f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
673f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
6741e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            dataBuffer, paddedWidth, paddedHeight);
675f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
676f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
677f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
678f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
679f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
680f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
681f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
682f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
683f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
684f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
685694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6865b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guybool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
6875b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
688694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
689694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
69009147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (!mCurrentFont) {
69109147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        LOGE("No font set");
6925b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        return false;
693694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
694694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6955b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
6965b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
69709147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
69851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
6995b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
700694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
701694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
702694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
703694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
704694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
7055b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
7065b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
707694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
708694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
70989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
71089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
71189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
71289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
71389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
71489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
71589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
71689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
71789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
71889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
719f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
72089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
72189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
72289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
723f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    float sigma = 0.3f * (float)radius + 0.6f;
72489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
72589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
72689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
72789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
72889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
72989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
73089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
73189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
73289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
73389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t r = -radius; r <= radius; r ++) {
7347975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
73589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
73689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
73789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
73889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
73989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
74089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
74189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t r = -radius; r <= radius; r ++) {
74289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
74389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
74489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
74589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
74689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
7471e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
74889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
74989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
75089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t y = 0; y < height; y ++) {
75289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
75489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
75589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for(int32_t x = 0; x < width; x ++) {
75789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
75889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
75989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
76089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            if ((x > radius) && (x < (width - radius))) {
76189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
76289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int r = -radius; r <= radius; r ++) {
7637975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
76489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
76589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
76689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
76789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
76889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
76989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int32_t r = -radius; r <= radius; r ++) {
77089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
77189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
77289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validW < 0) {
77389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
77489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
77589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validW > width - 1) {
77689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
77789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
77889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
77989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(input[validW]);
78089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
78189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
78289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
78389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
78489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
78589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
78689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
78789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
78889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
78989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
79089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
7911e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
79289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
79389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
79489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
79589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t y = 0; y < height; y ++) {
79689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
79789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
79889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
79989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for(int32_t x = 0; x < width; x ++) {
80089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
80189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
80289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
80389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
80489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            if ((y > radius) && (y < (height - radius))) {
80589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
80689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int32_t r = -radius; r <= radius; r ++) {
80789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
80889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
80989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
81089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
81189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
81289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
81389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int32_t r = -radius; r <= radius; r ++) {
81489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
81589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
81689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validH < 0) {
81789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
81889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
81989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validH > height - 1) {
82089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
82189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
82289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
82389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
82489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
82589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
82689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
82789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
82889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
82989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
83089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
83189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
83289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
83389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
83489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
83589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
83689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
83789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
83889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
83989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
84089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
84189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
84289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
84389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
84489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
84589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
846694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
847694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
848