FontRenderer.cpp revision 1e45aae5de003657e5d18f74d34998f5de5db5b7
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>
2251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include <utils/Log.h>
2351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
2451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include "FontRenderer.h"
2551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
26694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace android {
27694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace uirenderer {
28694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
29694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
3051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy// Defines
3151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
3251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_WIDTH 1024
3451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_HEIGHT 256
3551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
37694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Font
38694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
39694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
40694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
41694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mState(state), mFontId(fontId), mFontSize(fontSize) {
42694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
43694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
44694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
45694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::~Font() {
46694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
47694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (mState->mActiveFonts[ct] == this) {
48694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            mState->mActiveFonts.removeAt(ct);
49694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
50694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
51694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
52694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
53694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
5451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
55694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete glyph;
56694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
57694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
58694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
59694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::invalidateTextureCache() {
60694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
61694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCachedGlyphs.valueAt(i)->mIsValid = false;
62694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
63694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
64694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
65f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
66f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
67f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
68f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
69f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int width = (int) glyph->mBitmapWidth;
70f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int height = (int) glyph->mBitmapHeight;
71f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
7261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->bottom > nPenY) {
73f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->bottom = nPenY;
74f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
7561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->left > nPenX) {
76f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->left = nPenX;
77f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
7861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->right < nPenX + width) {
79f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->right = nPenX + width;
80f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->top < nPenY + height) {
82f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->top = nPenY + height;
83f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
84f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
85f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
86694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
87694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenX = x + glyph->mBitmapLeft;
88694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
89694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
9051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u1 = glyph->mBitmapMinU;
9151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u2 = glyph->mBitmapMaxU;
9251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v1 = glyph->mBitmapMinV;
9351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v2 = glyph->mBitmapMaxV;
9451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
9551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int width = (int) glyph->mBitmapWidth;
9651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int height = (int) glyph->mBitmapHeight;
9751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
9851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
9951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY, 0, u2, v2,
10051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX + width, nPenY - height, 0, u2, v1,
10151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            nPenX, nPenY - height, 0, u1, v1);
102694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
103694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
10489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
10561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
10689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
10789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
10889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
10989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
11089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
11189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
11289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t cacheWidth = mState->getCacheWidth();
11389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    const uint8_t* cacheBuffer = mState->getTextTextureData();
11489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
115f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint32_t cacheX = 0, cacheY = 0;
116f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int32_t bX = 0, bY = 0;
11789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
11889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
11961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy            if (bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
120f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                LOGE("Skipping invalid index");
121f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                continue;
122f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            }
12389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
12489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            bitmap[bY * bitmapW + bX] = tempCol;
12589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
12689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
12789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
12889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
12989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13065ef909776c03417d8b597738da54ca211e37e4fAlex SakhartchoukFont::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
1311e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    CachedGlyphInfo* cachedGlyph = NULL;
1321e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
1331e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (index >= 0) {
1341e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        cachedGlyph = mCachedGlyphs.valueAt(index);
1351e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    } else {
13665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        cachedGlyph = cacheGlyph(paint, utfChar);
13765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
13865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
13965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Is the glyph still in texture cache?
14065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if (!cachedGlyph->mIsValid) {
14165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
14265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
14365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
14465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return cachedGlyph;
14665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
14765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
14851769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
14961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
15061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
15161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
15261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy                bitmapW, bitmapH, NULL);
15361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    } else {
15461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
155f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
156f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
157f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
158f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
159f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
16061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, Rect *bounds) {
16161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds == NULL) {
162f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        LOGE("No return rectangle provided to measure text");
163f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        return;
164f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
165f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    bounds->set(1e6, -1e6, -1e6, 1e6);
166f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
167f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
168f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
169f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
17061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
17161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
172694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
173694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
174694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
175694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
176694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int penX = x, penY = y;
177694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int glyphsLeft = 1;
178694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs > 0) {
179694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        glyphsLeft = numGlyphs;
180694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
181694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
182694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    text += start;
183694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
184694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    while (glyphsLeft > 0) {
185694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
186694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
18761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        // Reached the end of the string
188694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (utfChar < 0) {
189694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
190694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
191694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
19265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
193694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
194694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
195694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (cachedGlyph->mIsValid) {
196f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            switch(mode) {
197f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case FRAMEBUFFER:
19889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                drawCachedGlyph(cachedGlyph, penX, penY);
199f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
200f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case BITMAP:
201f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
202f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
203f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case MEASURE:
204f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                measureCachedGlyph(cachedGlyph, penX, penY, bounds);
205f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
20689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
207694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
208694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
20909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        penX += SkFixedFloor(cachedGlyph->mAdvanceX);
210694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
211694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If we were given a specific number of glyphs, decrement
212694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (numGlyphs > 0) {
213694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            glyphsLeft--;
214694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
215694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
216694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
217694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
21851769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
219694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
220694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
221694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapLeft = skiaGlyph.fLeft;
222694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapTop = skiaGlyph.fTop;
223694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
224694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
225694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
226694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
227694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Get the bitmap for the glyph
228694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    paint->findImage(skiaGlyph);
22951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
230694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
231694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!glyph->mIsValid) {
232694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
233694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
234694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
235694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + skiaGlyph.fWidth;
236694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + skiaGlyph.fHeight;
237694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
23889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartX = startX;
23989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartY = startY;
240694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapWidth = skiaGlyph.fWidth;
241694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapHeight = skiaGlyph.fHeight;
242694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
24351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheWidth = mState->getCacheWidth();
24451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint32_t cacheHeight = mState->getCacheHeight();
245694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
246694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
247694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
248694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
249694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
250694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->mUploadTexture = true;
252694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
253694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25451769a68a5cb34e9564740c6a854fcb93018789dRomain GuyFont::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
25551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
256694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCachedGlyphs.add(glyph, newGlyph);
257694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
258694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
259694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mGlyphIndex = skiaGlyph.fID;
260694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mIsValid = false;
261694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
262694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    updateGlyphCache(paint, skiaGlyph, newGlyph);
263694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
264694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newGlyph;
265694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
266694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
267694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
268694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> &activeFonts = state->mActiveFonts;
269694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
270694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < activeFonts.size(); i++) {
27151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        Font* font = activeFonts[i];
27251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        if (font->mFontId == fontId && font->mFontSize == fontSize) {
27351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
274694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
275694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
276694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
277694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Font* newFont = new Font(state, fontId, fontSize);
278694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
279694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
280694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
281694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
282694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
283694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
284694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
285694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
286694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
28751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    LOGD("Creating FontRenderer");
28851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
289694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
290694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
2929b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextureId = 0;
293694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2949cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
2959cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextTexture = NULL;
2969cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
297694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
298694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
29951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
3009b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
30151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
30251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
30351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
30451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Setting text cache width to %s pixels", property);
30551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheWidth = atoi(property);
30651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
30751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
30851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
30951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
31051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
31151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Setting text cache width to %s pixels", property);
31251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        mCacheHeight = atoi(property);
31351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
31451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
31551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
316694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
317694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
318694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
319694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
320694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete mCacheLines[i];
321694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
322694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCacheLines.clear();
323694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3249cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
3259cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
3269cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextTexture;
3279cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    }
3289b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3299cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mTextureId) {
3309b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        glDeleteTextures(1, &mTextureId);
3319b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
332694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
333694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
334694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
335694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
336694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
337694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
338694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
339694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
340694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
341694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
342694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
343694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
344694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
345694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
346694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
347694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
348694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCacheLines[i]->mCurrentCol = 0;
349694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
350694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
351694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
35251769a68a5cb34e9564740c6a854fcb93018789dRomain Guybool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
353694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
354694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
355694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        LOGE("Font size to large to fit in cache. width, height = %i, %i",
356694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                (int) glyph.fWidth, (int) glyph.fHeight);
357694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return false;
358694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
359694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
360694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
361694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
362694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
363694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
364694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    bool bitmapFit = false;
365694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
366694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
367694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (bitmapFit) {
368694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
369694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
370694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
371694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
372694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
373694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!bitmapFit) {
374694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
375694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
376694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
377694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
378694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
379694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            if (bitmapFit) {
380694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                break;
381694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            }
382694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
383694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // if we still don't fit, something is wrong and we shouldn't draw
385694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (!bitmapFit) {
386694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            LOGE("Bitmap doesn't 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
392694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
393694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
394694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
395694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
396694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
397694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
398694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheWidth = mCacheWidth;
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
40089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* cacheBuffer = mTextTexture;
40189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
40789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
412694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return true;
413694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initTextTexture() {
41689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
417694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mUploadTexture = false;
418694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
419694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenTextures(1, &mTextureId);
420694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindTexture(GL_TEXTURE_2D, mTextureId);
421694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
4229b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Initialize texture dimentions
4239b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
42461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
425694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
426694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
427694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
428694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
429694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
430694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
431694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
432694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Split up our cache texture into lines of certain widths
433694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nextLine = 0;
43451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
435694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
43651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
437694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
43865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
43965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    nextLine += mCacheLines.top()->mMaxHeight;
44051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
441694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
44251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
443694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
44451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
44651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
447694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
448694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
449694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
450694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
451694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t numIndicies = mMaxNumberOfQuads * 6;
452694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
45351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
454694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
455694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
456694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
457694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
458694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
459694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
460694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
461694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
462694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
463694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
464694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
465694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
466694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
467694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
468694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
469694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
470694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
471694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
472694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ARRAY_BUFFER, 0);
473694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
474694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
475694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
476694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t coordSize = 3;
477694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
478694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
4799b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
4809b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
481694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
482694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
483694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
484694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
485694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
486694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
487694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
488694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
489694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
490694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
491694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
49265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // We store a string with letters in a rough frequency of occurrence
49365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
49465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
49565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16(",.?!()-+@;:`'");
49665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("0123456789");
49765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
498694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
499694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
500694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5019b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
5029b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    if (!mUploadTexture) {
5039b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
5049b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
5059b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5069b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    glBindTexture(GL_TEXTURE_2D, mTextureId);
5079b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5089b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Iterate over all the cache lines and see which ones need to be updated
5099b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
5109b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        CacheTextureLine* cl = mCacheLines[i];
5119b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        if(cl->mDirty) {
5129b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
5139b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t yOffset = cl->mCurrentRow;
5149b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t width   = mCacheWidth;
5159b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t height  = cl->mMaxHeight;
5161e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            void* textureData = mTextTexture + yOffset*width;
5179b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5189b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
5191e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
5209b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5219b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            cl->mDirty = false;
5229b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
523694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
524694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5259b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
5269b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
5279b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
5289b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
5299b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
5309b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
53151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* vtx = mTextMeshPtr;
53251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* tex = vtx + 3;
533694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
534694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // position is slot 0
535694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t slot = 0;
536694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
537694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
538694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // texture0 is slot 1
539694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    slot = 1;
540694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
541694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
542694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
543694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
544694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
545694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
546694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
547694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
548694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        float x4, float y4, float z4, float u4, float v4) {
54909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
55009147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        return;
55109147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    }
55209147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
553694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
554694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t floatsPerVert = 5;
55551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
556694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
557694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
558694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
559694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z1;
560694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
561694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
562694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
563694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
564694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
565694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z2;
566694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
567694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
568694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
569694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
570694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
571694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z3;
572694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
574694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
576694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
577694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = z4;
578694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
579694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
580694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
581694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
583694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
584694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
585694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
586694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
587694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
588694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
58965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukuint32_t FontRenderer::getRemainingCacheCapacity() {
59065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = 0;
59165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    float totalPixels = 0;
59265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
59365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
59465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         totalPixels += mCacheLines[i]->mMaxWidth;
59565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
59665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    remainingCapacity = (remainingCapacity * 100) / totalPixels;
59765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return remainingCapacity;
59865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
59965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
60065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::precacheLatin(SkPaint* paint) {
60165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Remaining capacity is measured in %
60265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = getRemainingCacheCapacity();
60365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t precacheIdx = 0;
60465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
60565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
60665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        remainingCapacity = getRemainingCacheCapacity();
60765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheIdx ++;
60865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
60965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
61065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
61165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
61265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t currentNumFonts = mActiveFonts.size();
613694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize);
61465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
61565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    const float maxPrecacheFontSize = 40.0f;
61665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    bool isNewFont = currentNumFonts != mActiveFonts.size();
61765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
61865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if(isNewFont && fontSize <= maxPrecacheFontSize ){
61965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheLatin(paint);
62065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
621694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
622f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
6231e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
6241e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
6251e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
6261e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
6271e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
6281e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
6291e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
6301e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
6311e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
6321e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
6331e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
6341e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
635f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
636f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
637f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
6381e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
6391e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
640f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
6411e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
642f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
643f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
6441e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
645f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
646f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
647f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
648f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
6491e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            dataBuffer, paddedWidth, paddedHeight);
650f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
651f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
652f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
653f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
654f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
655f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
656f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
657f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
658f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
659f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
660694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
66151769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
66251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
663694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
664694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
66509147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (!mCurrentFont) {
66609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        LOGE("No font set");
667694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
668694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
669694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
67009147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
67151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
672694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
673694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
674694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
675694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
676694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
677694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
678694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
67989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
68089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
68189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
68289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
68389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
68489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
68589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
68689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
68789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
68889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
689f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
69089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
69189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
69289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
693f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    float sigma = 0.3f * (float)radius + 0.6f;
69489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
69589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
69689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
69789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
69889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
69989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
70089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
70189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
70289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
70389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t r = -radius; r <= radius; r ++) {
70489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        float floatR = (float)r;
70589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
70689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
70789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
70889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
70989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
71089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
71189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t r = -radius; r <= radius; r ++) {
71289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
71389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
71489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
71589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
71689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
7171e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
71889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
71989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
72089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
72189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t y = 0; y < height; y ++) {
72289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
72389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
72489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
72589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
72689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for(int32_t x = 0; x < width; x ++) {
72789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
72889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
72989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
73089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            if ((x > radius) && (x < (width - radius))) {
73189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
73289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int r = -radius; r <= radius; r ++) {
73389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
73489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
73589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
73689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
73789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
73889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
73989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int32_t r = -radius; r <= radius; r ++) {
74089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
74189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
74289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validW < 0) {
74389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
74489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
74589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validW > width - 1) {
74689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
74789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
74889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
74989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(input[validW]);
75089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
75189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
75289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
75389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
75489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
75589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
75689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
75789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
75889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
75989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
7611e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
76289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
76389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
76489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for(int32_t y = 0; y < height; y ++) {
76689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
76889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
76989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for(int32_t x = 0; x < width; x ++) {
77089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
77189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
77289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
77389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
77489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            if ((y > radius) && (y < (height - radius))) {
77589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
77689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int32_t r = -radius; r <= radius; r ++) {
77789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
77889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
77989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
78089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
78189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
78289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
78389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                for(int32_t r = -radius; r <= radius; r ++) {
78489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
78589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
78689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validH < 0) {
78789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
78889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
78989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    if(validH > height - 1) {
79089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
79189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
79289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
79389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
79489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
79589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
79689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
79789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
79889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
79989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
80089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
80189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
80289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
80389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
80489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
80589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
80689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
80789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
80889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
80989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
81089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
81189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
81289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
81389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
81489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
81589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
816694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
817694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
818