FontRenderer.cpp revision 8668f8a633d9299091556c3b2e5ae07be8dce360
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,
438668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase        int flags, uint32_t italicStyle, uint32_t scaleX) :
442577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mState(state), mFontId(fontId), mFontSize(fontSize),
458668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
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,
2828668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase        int flags, uint32_t italicStyle, uint32_t scaleX) {
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 &&
2888668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase                font->mFlags == flags && font->mItalicStyle == italicStyle &&
2898668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase                font->mScaleX == scaleX) {
29051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
292694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
293694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2948668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
295694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
296694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
297694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
298694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
299694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
300694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
301694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
302694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
303514fb18827186591d66973c2362c859b64b63556Romain Guystatic bool sLogFontRendererCreate = true;
304514fb18827186591d66973c2362c859b64b63556Romain Guy
305694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
306c9855a53edfac818dc68714557185977556f849dRomain Guy    if (sLogFontRendererCreate) {
307c9855a53edfac818dc68714557185977556f849dRomain Guy        INIT_LOGD("Creating FontRenderer");
308c9855a53edfac818dc68714557185977556f849dRomain Guy    }
30951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
310b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
311694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
312694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
313694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
3149b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextureId = 0;
315694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3169cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
3179cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextTexture = NULL;
3189cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
319694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
320894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk    mPositionAttrSlot = -1;
321894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk    mTexcoordAttrSlot = -1;
322694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
32351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
3249b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
32551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
32651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
32751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
328c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
329c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
330c9855a53edfac818dc68714557185977556f849dRomain Guy        }
33151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheWidth = atoi(property);
33251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
333514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
334c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Using default text cache width of %i pixels", mCacheWidth);
335514fb18827186591d66973c2362c859b64b63556Romain Guy        }
33651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
33751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
33851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
339c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
340c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
341c9855a53edfac818dc68714557185977556f849dRomain Guy        }
34251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheHeight = atoi(property);
34351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
344514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
345c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Using default text cache height of %i pixels", mCacheHeight);
346514fb18827186591d66973c2362c859b64b63556Romain Guy        }
34751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
348514fb18827186591d66973c2362c859b64b63556Romain Guy
349514fb18827186591d66973c2362c859b64b63556Romain Guy    sLogFontRendererCreate = false;
350694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
351694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
352694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
353694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
354694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete mCacheLines[i];
355694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
356694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCacheLines.clear();
357694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3589cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
3599cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
3609cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextTexture;
3619cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    }
3629b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3639cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mTextureId) {
3649b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        glDeleteTextures(1, &mTextureId);
3659b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
366694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
367694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
368694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
369694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
370694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
371694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
372694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
373694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
374694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
375694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
376694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
377694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
378694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
379694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
380694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
381694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
382694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCacheLines[i]->mCurrentCol = 0;
383694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
385694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
38651769a68a5cb34e9564740c6a854fcb93018789dRomain Guybool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
387694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
3882bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
389694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        LOGE("Font size to large to fit in cache. width, height = %i, %i",
390694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                (int) glyph.fWidth, (int) glyph.fHeight);
391694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return false;
392694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
393694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
394694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
395694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
396694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
397694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
398694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    bool bitmapFit = false;
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
400694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
401694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (bitmapFit) {
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
407694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!bitmapFit) {
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
412694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
413694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            if (bitmapFit) {
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                break;
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            }
416694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
417694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
418694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // if we still don't fit, something is wrong and we shouldn't draw
419694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (!bitmapFit) {
420694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
421694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                    (int) glyph.fWidth, (int) glyph.fHeight);
422694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            return false;
423694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
424694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
425694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
426694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
427694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
428694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
429694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
430694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
431694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
432694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheWidth = mCacheWidth;
433694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
43489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* cacheBuffer = mTextTexture;
43589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
436694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
437694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
438694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
439694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
440694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
44189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
442b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
443694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
444694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
446694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return true;
447694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
448694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
449694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initTextTexture() {
45089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
4511de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
4521de1083e98cde9bdd5e8539dbc54fdea6531906eRomain Guy
453694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mUploadTexture = false;
454694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
455694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenTextures(1, &mTextureId);
456694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindTexture(GL_TEXTURE_2D, mTextureId);
457694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
4589b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Initialize texture dimentions
4599b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
46061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
461694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
462e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    mLinearFiltering = false;
463e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
464e8cb9c14309b0f01c0159efdf9a7198f44a62642Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
465694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
466694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
467694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
468694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
469694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Split up our cache texture into lines of certain widths
470694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nextLine = 0;
4717975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
472694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4737975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
474694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4757975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
47665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    nextLine += mCacheLines.top()->mMaxHeight;
4777975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
478694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4797975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
480694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
4817975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
482694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
48351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
484694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
485694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
486694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
487694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
488694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t numIndicies = mMaxNumberOfQuads * 6;
489694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
49051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
491694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
492694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
493694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
494694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
495694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
496694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
497694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
498694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
499694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
500694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
501694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
502694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
503694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
504694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
505694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
506694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
5075d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
5085d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
5095d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
510694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
511694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
512694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
513694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t coordSize = 3;
514694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
515694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
5169b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
5179b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
518694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
519694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
520694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
521694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
522694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
523694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
524694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
525694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
526694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
527694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
528694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
52965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // We store a string with letters in a rough frequency of occurrence
53065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
53165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
53265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16(",.?!()-+@;:`'");
53365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("0123456789");
53465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
535694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
536694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
537694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5389b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
5399b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    if (!mUploadTexture) {
5409b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
5419b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
5429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5439b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glBindTexture(GL_TEXTURE_2D, mTextureId);
5449b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5459b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Iterate over all the cache lines and see which ones need to be updated
5469b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
5479b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        CacheTextureLine* cl = mCacheLines[i];
5489b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        if(cl->mDirty) {
5499b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
5509b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t yOffset = cl->mCurrentRow;
5519b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t width   = mCacheWidth;
5529b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t height  = cl->mMaxHeight;
5531e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            void* textureData = mTextTexture + yOffset*width;
5549b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5559b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
5561e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
5579b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5589b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            cl->mDirty = false;
5599b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
560694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
561694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5629b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
5639b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
5649b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5659b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
5669b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
5679b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
56851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* vtx = mTextMeshPtr;
56951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* tex = vtx + 3;
570694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
571894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk    glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
572894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk    glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
574694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
5765b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
5775b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
578694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
579694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
580694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
581694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float x4, float y4, float z4, float u4, float v4) {
58309147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
58409147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        return;
58509147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    }
58609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
587694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
588694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t floatsPerVert = 5;
58951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
590694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
591694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
592694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
593694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z1;
594694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
595694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
596694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
597694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
598694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
599694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z2;
600694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
601694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
602694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
603694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
604694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
605694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z3;
606694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
607694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
608694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
609694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
610694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
611694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z4;
612694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
613694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
614694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
615694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
616694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6175b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
6185b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
6195b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
6205b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
6215b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
6225b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
6235b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
624694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
625694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
626694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
627694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
628694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
629694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
63065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukuint32_t FontRenderer::getRemainingCacheCapacity() {
63165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = 0;
63265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    float totalPixels = 0;
63365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
63465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
63565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         totalPixels += mCacheLines[i]->mMaxWidth;
63665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
63765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    remainingCapacity = (remainingCapacity * 100) / totalPixels;
63865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return remainingCapacity;
63965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
64065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
64165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::precacheLatin(SkPaint* paint) {
64265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Remaining capacity is measured in %
64365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = getRemainingCacheCapacity();
64465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t precacheIdx = 0;
645054dc1840941665e32036f9523df51720ad069c8Romain Guy    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
646054dc1840941665e32036f9523df51720ad069c8Romain Guy        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
64765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        remainingCapacity = getRemainingCacheCapacity();
64865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheIdx ++;
64965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
65065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
65165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
65265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
65365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t currentNumFonts = mActiveFonts.size();
654325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    int flags = 0;
655325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    if (paint->isFakeBoldText()) {
656325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        flags |= Font::kFakeBold;
657325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    }
6582577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy
6592577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    const float skewX = paint->getTextSkewX();
6602577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    uint32_t italicStyle = *(uint32_t*) &skewX;
6618668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    const float scaleXFloat = paint->getTextScaleX();
6628668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
6638668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
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
721894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk    if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
722894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk        LOGE("Font renderer unable to draw, attribute slots undefined");
723894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk        return false;
724894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk    }
725894df17eee708688c8a6e67941add2017239c790Alex Sakhartchouk
7265b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
7275b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
72809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
72951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
7305b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
731694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
732694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
733694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
734694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
735694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
7365b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
7375b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
738694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
739694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
74089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
74189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
74289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
74389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
74489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
74589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
74689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
74789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
74889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
74989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
750f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
75189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
75289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
75389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
754325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    float sigma = 0.3f * (float) radius + 0.6f;
75589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
75789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
75889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
75989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
76089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
76189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
76289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
764325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
7657975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
76689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
76789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
76889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
76989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
77089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
77189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
772325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
77389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
77489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
77589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
77689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
77789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
7781e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
77989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
78089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
78189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
782325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
78389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
78489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
78589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
78689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
787325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
78889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
78989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
79089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
791325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (x > radius && x < (width - radius)) {
79289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
793325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int r = -radius; r <= radius; r ++) {
7947975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
79589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
79689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
79789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
79889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
79989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
800325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
80189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
80289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
803325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW < 0) {
80489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
80589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
806325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW > width - 1) {
80789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
80889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
80989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
810325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) input[validW];
81189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
81289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
81389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
81489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
81589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
81689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
81789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
81889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
81989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
82089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
82189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
8221e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
82389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
82489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
82589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
826325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
82789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
82889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
82989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
830325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
83189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
83289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
83389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
83489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
835325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (y > radius && y < (height - radius)) {
83689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
837325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
83889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
83989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
84089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
84189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
84289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
84389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
844325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
84589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
84689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
847325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH < 0) {
84889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
84989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
850325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH > height - 1) {
85189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
85289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
85389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
85489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
855325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) (*i);
85689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
85789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
85889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
85989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
860325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            *output = (uint8_t) blurredPixel;
86189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
86289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
86389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
86489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
86589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
86689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
86789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
86889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
86989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
87089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
87189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
87289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
87389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
87489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
87589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
87689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
877694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
878694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
879