FontRenderer.cpp revision c9855a53edfac818dc68714557185977556f849d
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
25c9855a53edfac818dc68714557185977556f849dRomain Guy#include "Debug.h"
2651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include "FontRenderer.h"
2751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
28694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace android {
29694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace uirenderer {
30694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
31694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
3251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy// Defines
3351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
3451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_WIDTH 1024
3651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_HEIGHT 256
3751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
39694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Font
40694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
41694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
422577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont::Font(FontRenderer* state, uint32_t fontId, float fontSize,
432577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        int flags, uint32_t italicStyle) :
442577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mState(state), mFontId(fontId), mFontSize(fontSize),
452577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mFlags(flags), mItalicStyle(italicStyle) {
46694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
47694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
48694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
49694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::~Font() {
50694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
51694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (mState->mActiveFonts[ct] == this) {
52694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            mState->mActiveFonts.removeAt(ct);
53694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
54694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
55694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
56694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
57694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
5851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
59694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete glyph;
60694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
61694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
62694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
63694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::invalidateTextureCache() {
64694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
65694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCachedGlyphs.valueAt(i)->mIsValid = false;
66694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
67694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
68694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
69f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
70f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
71f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
72f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
73f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int width = (int) glyph->mBitmapWidth;
74f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int height = (int) glyph->mBitmapHeight;
75f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
7661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->bottom > nPenY) {
77f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->bottom = nPenY;
78f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
7961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->left > nPenX) {
80f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->left = nPenX;
81f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->right < nPenX + width) {
83f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->right = nPenX + width;
84f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->top < nPenY + height) {
86f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->top = nPenY + height;
87f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
88f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
89f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
90694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
91694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenX = x + glyph->mBitmapLeft;
92694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
93694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
9451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u1 = glyph->mBitmapMinU;
9551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u2 = glyph->mBitmapMaxU;
9651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v1 = glyph->mBitmapMinV;
9751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v2 = glyph->mBitmapMaxV;
9851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
9951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int width = (int) glyph->mBitmapWidth;
10051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int height = (int) glyph->mBitmapHeight;
10151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
10251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
10351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY, 0, u2, v2,
10451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY - height, 0, u2, v1,
10551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX, nPenY - height, 0, u1, v1);
106694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
107694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
108b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
109b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
11089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
11189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
11289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
11489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
11589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t cacheWidth = mState->getCacheWidth();
11789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    const uint8_t* cacheBuffer = mState->getTextTextureData();
11889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
119f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint32_t cacheX = 0, cacheY = 0;
120f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int32_t bX = 0, bY = 0;
12189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
12289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
123b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
124f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                LOGE("Skipping invalid index");
125f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                continue;
126f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            }
12789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
12889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            bitmap[bY * bitmapW + bX] = tempCol;
12989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
13089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
13189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
13389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13465ef909776c03417d8b597738da54ca211e37e4fAlex SakhartchoukFont::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
1351e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    CachedGlyphInfo* cachedGlyph = NULL;
1361e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
1371e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (index >= 0) {
1381e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        cachedGlyph = mCachedGlyphs.valueAt(index);
1391e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    } else {
14065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        cachedGlyph = cacheGlyph(paint, utfChar);
14165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
14265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Is the glyph still in texture cache?
14465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if (!cachedGlyph->mIsValid) {
14565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
14665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
14765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
14865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return cachedGlyph;
15065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
15165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
15251769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
15361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
15461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
15561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
15661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy                bitmapW, bitmapH, NULL);
15761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    } else {
15861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
159f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
160f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
161f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
162f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
163f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
16461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, Rect *bounds) {
16561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds == NULL) {
166f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        LOGE("No return rectangle provided to measure text");
167f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        return;
168f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
169f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    bounds->set(1e6, -1e6, -1e6, 1e6);
170f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
171f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
172f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
17358ef7fbf16864164efe98bf613b15c64deb1afc0Romain Guy#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
1742bffd268f135df8308c9e67af110525a5c463424Romain Guy
175f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
17661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
17761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
178694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
179694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
180694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
181694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1822bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed penX = SkIntToFixed(x);
1832bffd268f135df8308c9e67af110525a5c463424Romain Guy    int penY = y;
184694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int glyphsLeft = 1;
185694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs > 0) {
186694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        glyphsLeft = numGlyphs;
187694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
188694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1892bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed prevRsbDelta = 0;
1902bffd268f135df8308c9e67af110525a5c463424Romain Guy    penX += SK_Fixed1 / 2;
1912bffd268f135df8308c9e67af110525a5c463424Romain Guy
192694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    text += start;
193694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
194694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    while (glyphsLeft > 0) {
195694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
196694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
19761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        // Reached the end of the string
198694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (utfChar < 0) {
199694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
200694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
201694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
20265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
2032bffd268f135df8308c9e67af110525a5c463424Romain Guy        penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
2042bffd268f135df8308c9e67af110525a5c463424Romain Guy        prevRsbDelta = cachedGlyph->mRsbDelta;
205694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
206694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
207694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (cachedGlyph->mIsValid) {
208f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            switch(mode) {
209f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case FRAMEBUFFER:
2102bffd268f135df8308c9e67af110525a5c463424Romain Guy                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
211f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
212f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case BITMAP:
2132bffd268f135df8308c9e67af110525a5c463424Romain Guy                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
214f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
215f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case MEASURE:
2162bffd268f135df8308c9e67af110525a5c463424Romain Guy                measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
217f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
21889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
219694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
220694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2212bffd268f135df8308c9e67af110525a5c463424Romain Guy        penX += cachedGlyph->mAdvanceX;
222694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
223694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If we were given a specific number of glyphs, decrement
224694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (numGlyphs > 0) {
225694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            glyphsLeft--;
226694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
227694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
228694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
229694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
23051769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
231694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
232694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
233694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapLeft = skiaGlyph.fLeft;
234694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapTop = skiaGlyph.fTop;
2352bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
2362bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
237694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
238694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
239694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
240694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
241694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Get the bitmap for the glyph
242694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    paint->findImage(skiaGlyph);
24351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
244694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
245694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!glyph->mIsValid) {
246694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
247694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
248694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
249694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + skiaGlyph.fWidth;
250694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + skiaGlyph.fHeight;
251694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartX = startX;
25389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartY = startY;
254694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapWidth = skiaGlyph.fWidth;
255694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapHeight = skiaGlyph.fHeight;
256694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheWidth = mState->getCacheWidth();
25851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheHeight = mState->getCacheHeight();
259694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
260694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
261694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
262694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
263694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
264694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
26551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->mUploadTexture = true;
266694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
267694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
26851769a68a5cb34e9564740c6a854fcb93018789dRomain GuyFont::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
26951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
270694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCachedGlyphs.add(glyph, newGlyph);
271694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
272694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
273694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mGlyphIndex = skiaGlyph.fID;
274694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mIsValid = false;
275694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
276694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    updateGlyphCache(paint, skiaGlyph, newGlyph);
277694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
278694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newGlyph;
279694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
280694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2812577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
2822577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        int flags, uint32_t italicStyle) {
283694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> &activeFonts = state->mActiveFonts;
284694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
285694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < activeFonts.size(); i++) {
28651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        Font* font = activeFonts[i];
2872577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        if (font->mFontId == fontId && font->mFontSize == fontSize &&
2882577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy                font->mFlags == flags && font->mItalicStyle == italicStyle) {
28951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
290694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
292694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2932577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
294694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
295694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
296694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
297694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
298694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
299694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
300694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
301694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
302514fb18827186591d66973c2362c859b64b63556Romain Guystatic bool sLogFontRendererCreate = true;
303514fb18827186591d66973c2362c859b64b63556Romain Guy
304694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
305c9855a53edfac818dc68714557185977556f849dRomain Guy    if (sLogFontRendererCreate) {
306c9855a53edfac818dc68714557185977556f849dRomain Guy        INIT_LOGD("Creating FontRenderer");
307c9855a53edfac818dc68714557185977556f849dRomain Guy    }
30851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
309b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
310694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
311694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
312694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
3139b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextureId = 0;
314694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3159cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
3169cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextTexture = NULL;
3179cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
318694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
319694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
32051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
3219b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
32251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
32351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
32451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
325c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
326c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
327c9855a53edfac818dc68714557185977556f849dRomain Guy        }
32851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheWidth = atoi(property);
32951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
330514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
331c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Using default text cache width of %i pixels", mCacheWidth);
332514fb18827186591d66973c2362c859b64b63556Romain Guy        }
33351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
33451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
33551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
336c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
337c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
338c9855a53edfac818dc68714557185977556f849dRomain Guy        }
33951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheHeight = atoi(property);
34051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
341514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
342c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Using default text cache height of %i pixels", mCacheHeight);
343514fb18827186591d66973c2362c859b64b63556Romain Guy        }
34451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
345514fb18827186591d66973c2362c859b64b63556Romain Guy
346514fb18827186591d66973c2362c859b64b63556Romain Guy    sLogFontRendererCreate = false;
347694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
348694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
349694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
350694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
351694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete mCacheLines[i];
352694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
353694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCacheLines.clear();
354694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3559cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
3569cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
3579cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextTexture;
3589cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    }
3599b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3609cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mTextureId) {
3619b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        glDeleteTextures(1, &mTextureId);
3629b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
363694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
364694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
365694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
366694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
367694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
368694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
369694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
370694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
371694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
372694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
373694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
374694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
375694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
376694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
377694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
378694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
379694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCacheLines[i]->mCurrentCol = 0;
380694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
381694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
382694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
38351769a68a5cb34e9564740c6a854fcb93018789dRomain Guybool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
3852bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
386694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        LOGE("Font size to large to fit in cache. width, height = %i, %i",
387694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                (int) glyph.fWidth, (int) glyph.fHeight);
388694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return false;
389694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
390694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
391694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
392694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
393694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
394694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
395694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    bool bitmapFit = false;
396694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
397694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
398694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (bitmapFit) {
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
400694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
401694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!bitmapFit) {
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
407694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            if (bitmapFit) {
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                break;
412694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            }
413694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // if we still don't fit, something is wrong and we shouldn't draw
416694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (!bitmapFit) {
417694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
418694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                    (int) glyph.fWidth, (int) glyph.fHeight);
419694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            return false;
420694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
421694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
422694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
423694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
424694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
425694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
426694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
427694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
428694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
429694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheWidth = mCacheWidth;
430694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
43189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* cacheBuffer = mTextTexture;
43289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
433694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
434694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
435694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
436694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
437694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
43889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
439b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
440694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
441694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
442694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
443694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return true;
444694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
446694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initTextTexture() {
44789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
4481de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
4491de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy
450694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mUploadTexture = false;
451694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
452694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenTextures(1, &mTextureId);
453694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindTexture(GL_TEXTURE_2D, mTextureId);
454694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
4559b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Initialize texture dimentions
4569b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
45761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
458694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
459e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    mLinearFiltering = false;
460e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
461e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
462694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
463694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
464694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
465694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
466694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Split up our cache texture into lines of certain widths
467694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nextLine = 0;
4687975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
469694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4707975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
471694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4727975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
47365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    nextLine += mCacheLines.top()->mMaxHeight;
4747975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
475694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4767975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
477694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4787975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
479694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
48051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
481694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
482694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
483694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
484694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
485694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t numIndicies = mMaxNumberOfQuads * 6;
486694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
48751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
488694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
489694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
490694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
491694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
492694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
493694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
494694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
495694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
496694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
497694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
498694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
499694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
500694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
501694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
502694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
503694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
5045d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
5055d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
5065d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
507694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
508694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
509694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
510694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t coordSize = 3;
511694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
512694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
5139b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
5149b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
515694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
516694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
517694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
518694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
519694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
520694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
521694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
522694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
523694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
524694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
525694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
52665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // We store a string with letters in a rough frequency of occurrence
52765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
52865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
52965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16(",.?!()-+@;:`'");
53065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("0123456789");
53165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
532694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
533694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
534694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5359b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
5369b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    if (!mUploadTexture) {
5379b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
5389b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
5399b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5409b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glBindTexture(GL_TEXTURE_2D, mTextureId);
5419b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Iterate over all the cache lines and see which ones need to be updated
5439b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
5449b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        CacheTextureLine* cl = mCacheLines[i];
5459b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        if(cl->mDirty) {
5469b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
5479b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t yOffset = cl->mCurrentRow;
5489b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t width   = mCacheWidth;
5499b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t height  = cl->mMaxHeight;
5501e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            void* textureData = mTextTexture + yOffset*width;
5519b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5529b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
5531e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
5549b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5559b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            cl->mDirty = false;
5569b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
557694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
558694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5599b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
5609b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
5619b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5629b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
5639b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
5649b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
56551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* vtx = mTextMeshPtr;
56651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* tex = vtx + 3;
567694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
568694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // position is slot 0
569694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t slot = 0;
570694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
571694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
572694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // texture0 is slot 1
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    slot = 1;
574694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
576694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
577694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
5785b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
5795b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
580694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
581694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
583694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
584694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float x4, float y4, float z4, float u4, float v4) {
58509147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
58609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        return;
58709147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    }
58809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
589694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
590694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t floatsPerVert = 5;
59151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
592694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
593694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
594694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
595694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z1;
596694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
597694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
598694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
599694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
600694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
601694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z2;
602694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
603694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
604694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
605694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
606694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
607694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z3;
608694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
609694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
610694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
611694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
612694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
613694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z4;
614694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
615694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
616694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
617694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
618694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6195b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
6205b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
6215b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
6225b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
6235b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
6245b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
6255b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
626694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
627694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
628694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
629694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
630694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
631694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
63265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukuint32_t FontRenderer::getRemainingCacheCapacity() {
63365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = 0;
63465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    float totalPixels = 0;
63565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
63665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
63765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         totalPixels += mCacheLines[i]->mMaxWidth;
63865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
63965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    remainingCapacity = (remainingCapacity * 100) / totalPixels;
64065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return remainingCapacity;
64165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
64265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
64365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::precacheLatin(SkPaint* paint) {
64465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Remaining capacity is measured in %
64565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = getRemainingCacheCapacity();
64665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t precacheIdx = 0;
647054dc1840941665e32036f9523df51720ad069c8Romain Guy    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
648054dc1840941665e32036f9523df51720ad069c8Romain Guy        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
64965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        remainingCapacity = getRemainingCacheCapacity();
65065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheIdx ++;
65165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
65265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
65365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
65465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
65565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t currentNumFonts = mActiveFonts.size();
656325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    int flags = 0;
657325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    if (paint->isFakeBoldText()) {
658325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        flags |= Font::kFakeBold;
659325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    }
6602577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy
6612577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    const float skewX = paint->getTextSkewX();
6622577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    uint32_t italicStyle = *(uint32_t*) &skewX;
6632577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
66465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
66565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    const float maxPrecacheFontSize = 40.0f;
66665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    bool isNewFont = currentNumFonts != mActiveFonts.size();
66765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
6682bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (isNewFont && fontSize <= maxPrecacheFontSize) {
66965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheLatin(paint);
67065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
671694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
6727975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy
673f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
6741e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
6751e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
6761e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
6771e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
6781e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
6791e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
6801e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
6811e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
6821e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
6831e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
6841e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
6851e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
686f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
687f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
688f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
6891e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
6901e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
691f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
6921e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
693f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
694f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
6951e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
696f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
697f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
698f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
699f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
7001e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            dataBuffer, paddedWidth, paddedHeight);
701f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
702f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
703f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
704f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
705f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
706f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
707f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
708f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
709f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
710f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
711694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
7125b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guybool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
7135b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
714694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
715694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
71609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (!mCurrentFont) {
71709147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        LOGE("No font set");
7185b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        return false;
719694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
720694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
7215b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
7225b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
72309147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
72451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
7255b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
726694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
727694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
728694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
729694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
730694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
7315b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
7325b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
733694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
734694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
73589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
73689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
73789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
73889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
73989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
74089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
74189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
74289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
74389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
74489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
745f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
74689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
74789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
74889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
749325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    float sigma = 0.3f * (float) radius + 0.6f;
75089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
75289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
75389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
75489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
75589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
75689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
75789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
759325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
7607975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
76189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
76289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
76389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
76489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
76689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
767325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
76889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
76989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
77089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
77189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
77289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
7731e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
77489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
77589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
77689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
777325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
77889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
77989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
78089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
78189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
782325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
78389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
78489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
78589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
786325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (x > radius && x < (width - radius)) {
78789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
788325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int r = -radius; r <= radius; r ++) {
7897975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
79089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
79189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
79289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
79389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
79489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
795325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
79689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
79789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
798325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW < 0) {
79989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
80089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
801325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW > width - 1) {
80289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
80389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
80489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
805325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) input[validW];
80689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
80789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
80889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
80989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
81089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
81189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
81289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
81389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
81489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
81589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
81689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
8171e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
81889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
81989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
82089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
821325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
82289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
82389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
82489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
825325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
82689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
82789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
82889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
82989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
830325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (y > radius && y < (height - radius)) {
83189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
832325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
83389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
83489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
83589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
83689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
83789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
83889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
839325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
84089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
84189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
842325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH < 0) {
84389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
84489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
845325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH > height - 1) {
84689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
84789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
84889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
84989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
850325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) (*i);
85189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
85289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
85389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
85489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
855325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            *output = (uint8_t) blurredPixel;
85689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
85789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
85889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
85989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
86089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
86189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
86289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
86389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
86489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
86589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
86689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
86789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
86889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
86989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
87089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
87189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
872694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
873694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
874