FontRenderer.cpp revision 2a47c14e2a6f152496b43104bc785c488583fd59
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
2515bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy#include "Caches.h"
26c9855a53edfac818dc68714557185977556f849dRomain Guy#include "Debug.h"
2751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#include "FontRenderer.h"
287de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase#include "Caches.h"
2951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
30694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace android {
31694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace uirenderer {
32694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
33694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
3451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy// Defines
3551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
3651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
3751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_WIDTH 1024
3851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy#define DEFAULT_TEXT_CACHE_HEIGHT 256
3944984ea0cb3702384d023b5f211deda3c4b0b656Chet Haase#define MAX_TEXT_CACHE_WIDTH 2048
407de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase#define TEXTURE_BORDER_SIZE 2
417de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
427de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase///////////////////////////////////////////////////////////////////////////////
437de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase// CacheTextureLine
447de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase///////////////////////////////////////////////////////////////////////////////
457de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
467de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasebool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
477de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
487de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        return false;
497de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
507de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
517de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
527de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        *retOriginX = mCurrentCol + 1;
537de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        *retOriginY = mCurrentRow + 1;
547de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
557de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mDirty = true;
567de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        return true;
577de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
587de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
597de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    return false;
607de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
6144984ea0cb3702384d023b5f211deda3c4b0b656Chet Haase
6251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
63694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Font
64694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
65694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
662577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont::Font(FontRenderer* state, uint32_t fontId, float fontSize,
67bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        int flags, uint32_t italicStyle, uint32_t scaleX,
68bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        SkPaint::Style style, uint32_t strokeWidth) :
692577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mState(state), mFontId(fontId), mFontSize(fontSize),
70bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
71bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        mStyle(style), mStrokeWidth(mStrokeWidth) {
72694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
73694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
74694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
75694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::~Font() {
76694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
77694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (mState->mActiveFonts[ct] == this) {
78694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            mState->mActiveFonts.removeAt(ct);
79694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
80694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
81694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
82694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
83694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
84726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        delete mCachedGlyphs.valueAt(i);
85694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
86694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
87694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
88694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::invalidateTextureCache() {
89694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
90694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCachedGlyphs.valueAt(i)->mIsValid = false;
91694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
92694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
93694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
94f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchoukvoid Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
95f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
96f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
97f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
98f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int width = (int) glyph->mBitmapWidth;
99f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int height = (int) glyph->mBitmapHeight;
100f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
10161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->bottom > nPenY) {
102f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->bottom = nPenY;
103f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
10461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->left > nPenX) {
105f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->left = nPenX;
106f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
10761c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->right < nPenX + width) {
108f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->right = nPenX + width;
109f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
11061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->top < nPenY + height) {
111f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->top = nPenY + height;
112f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
113f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
114f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
115694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
116694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenX = x + glyph->mBitmapLeft;
117694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
118694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
11951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u1 = glyph->mBitmapMinU;
12051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u2 = glyph->mBitmapMaxU;
12151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v1 = glyph->mBitmapMinV;
12251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v2 = glyph->mBitmapMaxV;
12351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
12451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int width = (int) glyph->mBitmapWidth;
12551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int height = (int) glyph->mBitmapHeight;
12651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
127d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
128d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy            nPenX + width, nPenY, u2, v2,
129d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy            nPenX + width, nPenY - height, u2, v1,
1307de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
131694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
132694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
133b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
134b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
13589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
13689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
13789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
13889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
13989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
14089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1417de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
1427de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint32_t cacheWidth = cacheTexture->mWidth;
1437de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    const uint8_t* cacheBuffer = cacheTexture->mTexture;
14489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
145f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint32_t cacheX = 0, cacheY = 0;
146f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int32_t bX = 0, bY = 0;
14789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
14889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
149b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
150f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                LOGE("Skipping invalid index");
151f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                continue;
152f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            }
15389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
15489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            bitmap[bY * bitmapW + bX] = tempCol;
15589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
15689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
15789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
15889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1597de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
1601e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    CachedGlyphInfo* cachedGlyph = NULL;
161726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
1621e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (index >= 0) {
1631e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        cachedGlyph = mCachedGlyphs.valueAt(index);
1641e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    } else {
165726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        cachedGlyph = cacheGlyph(paint, textUnit);
16665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
16765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
16865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Is the glyph still in texture cache?
16965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if (!cachedGlyph->mIsValid) {
170726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
17165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
17265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
17365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
17465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return cachedGlyph;
17565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
17665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
177726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guyvoid Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
17861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
17961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
180726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
18161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy                bitmapW, bitmapH, NULL);
18261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    } else {
183726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
184726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy                0, 0, NULL);
185f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
186f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
187f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
188726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guyvoid Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
18961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, Rect *bounds) {
19061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds == NULL) {
191f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        LOGE("No return rectangle provided to measure text");
192f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        return;
193f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
194f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    bounds->set(1e6, -1e6, -1e6, 1e6);
195726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
196f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
197f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
19858ef7fbf16864164efe98bf613b15c64deb1afc0Romain Guy#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
1992bffd268f135df8308c9e67af110525a5c463424Romain Guy
200726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guyvoid Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
20161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
20261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
203694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
204694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
205694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
206694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2075a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy    float penX = x;
2082bffd268f135df8308c9e67af110525a5c463424Romain Guy    int penY = y;
209694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int glyphsLeft = 1;
210694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs > 0) {
211694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        glyphsLeft = numGlyphs;
212694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
213694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2142bffd268f135df8308c9e67af110525a5c463424Romain Guy    SkFixed prevRsbDelta = 0;
2155a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy    penX += 0.5f;
2162bffd268f135df8308c9e67af110525a5c463424Romain Guy
217694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    text += start;
218694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
219694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    while (glyphsLeft > 0) {
220726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        glyph_t glyph = GET_GLYPH(text);
221694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
22261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        // Reached the end of the string
223726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        if (IS_END_OF_STRING(glyph)) {
224694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
225694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
226694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
227726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
2285a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy        penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
2292bffd268f135df8308c9e67af110525a5c463424Romain Guy        prevRsbDelta = cachedGlyph->mRsbDelta;
230694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
231694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
232694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (cachedGlyph->mIsValid) {
233f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            switch(mode) {
234f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case FRAMEBUFFER:
2355a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
236f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
237f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case BITMAP:
2385a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
239f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
240f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            case MEASURE:
2415a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy                measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
242f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
24389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
244694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
245694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2465a6d3a4c0311318e6f1d4ee06aa47606e055a81bRomain Guy        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
247694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
248694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // If we were given a specific number of glyphs, decrement
249694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (numGlyphs > 0) {
250694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            glyphsLeft--;
251694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
252694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
253694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
254694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25551769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
256694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
257694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
258694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapLeft = skiaGlyph.fLeft;
259694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapTop = skiaGlyph.fTop;
2602bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
2612bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
262694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
263694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
264694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
265694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
266694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Get the bitmap for the glyph
267694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    paint->findImage(skiaGlyph);
2687de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
269694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
270694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!glyph->mIsValid) {
271694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
272694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
273694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
274694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + skiaGlyph.fWidth;
275694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + skiaGlyph.fHeight;
276694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
27789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartX = startX;
27889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartY = startY;
279694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapWidth = skiaGlyph.fWidth;
280694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapHeight = skiaGlyph.fHeight;
281694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2827de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
2837de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
284694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
285694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
286694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
287694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
288694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
289694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
29051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->mUploadTexture = true;
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
292694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2937de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
29451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
295694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCachedGlyphs.add(glyph, newGlyph);
296694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
297726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
298694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mGlyphIndex = skiaGlyph.fID;
299694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mIsValid = false;
300694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
301694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    updateGlyphCache(paint, skiaGlyph, newGlyph);
302694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
303694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newGlyph;
304694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
305694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3062577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
307bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        int flags, uint32_t italicStyle, uint32_t scaleX,
308bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        SkPaint::Style style, uint32_t strokeWidth) {
309694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> &activeFonts = state->mActiveFonts;
310694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
311694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < activeFonts.size(); i++) {
31251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        Font* font = activeFonts[i];
3132577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        if (font->mFontId == fontId && font->mFontSize == fontSize &&
3148668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase                font->mFlags == flags && font->mItalicStyle == italicStyle &&
315bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy                font->mScaleX == scaleX && font->mStyle == style &&
316bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
31751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
318694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
319694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
320694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
321bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
322bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy            scaleX, style, strokeWidth);
323694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
324694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
325694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
326694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
327694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
328694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
329694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
330694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
331514fb18827186591d66973c2362c859b64b63556Romain Guystatic bool sLogFontRendererCreate = true;
332514fb18827186591d66973c2362c859b64b63556Romain Guy
333694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
334c9855a53edfac818dc68714557185977556f849dRomain Guy    if (sLogFontRendererCreate) {
335c9855a53edfac818dc68714557185977556f849dRomain Guy        INIT_LOGD("Creating FontRenderer");
336c9855a53edfac818dc68714557185977556f849dRomain Guy    }
33751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
338b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
339694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
340694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
341694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
342694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3439cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
3447de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCurrentCacheTexture = NULL;
3457de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mLastCacheTexture = NULL;
3467de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTextureSmall = NULL;
3477de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTexture128 = NULL;
3487de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTexture256 = NULL;
3497de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTexture512 = NULL;
3509cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
3512a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    mLinearFiltering = false;
3522a47c14e2a6f152496b43104bc785c488583fd59Chet Haase
353694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
354694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3557de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
3567de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
35751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
35851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
35951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
360c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
361c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
362c9855a53edfac818dc68714557185977556f849dRomain Guy        }
3637de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mSmallCacheWidth = atoi(property);
36451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
365514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
3667de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
367514fb18827186591d66973c2362c859b64b63556Romain Guy        }
36851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
36951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
37051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
371c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
372c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
373c9855a53edfac818dc68714557185977556f849dRomain Guy        }
3747de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mSmallCacheHeight = atoi(property);
37551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
376514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
3777de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
378514fb18827186591d66973c2362c859b64b63556Romain Guy        }
37951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
380514fb18827186591d66973c2362c859b64b63556Romain Guy
381514fb18827186591d66973c2362c859b64b63556Romain Guy    sLogFontRendererCreate = false;
382694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
383694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
385694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
386694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete mCacheLines[i];
387694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
388694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCacheLines.clear();
389694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3909cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
3919cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
3927de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTextureSmall;
3937de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTexture128;
3947de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTexture256;
3957de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTexture512;
3969b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
397694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
398694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
400694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
401694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
407694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
412694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
413694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCacheLines[i]->mCurrentCol = 0;
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
416694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4172a47c14e2a6f152496b43104bc785c488583fd59Chet Haasevoid FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
4182a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    int width = cacheTexture->mWidth;
4192a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    int height = cacheTexture->mHeight;
4202a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    cacheTexture->mTexture = new uint8_t[width * height];
4212a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
4222a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
4232a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
4242a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    // Initialize texture dimensions
4252a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
4262a47c14e2a6f152496b43104bc785c488583fd59Chet Haase            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
4272a47c14e2a6f152496b43104bc785c488583fd59Chet Haase
4282a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
4292a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
4302a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
4317de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
4322a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4332a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4347de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
4357de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
4367de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasevoid FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
4377de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        uint32_t* retOriginX, uint32_t* retOriginY) {
4387de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mIsValid = false;
439694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
4407de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
4417de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        LOGE("Font size to large to fit in cache. width, height = %i, %i",
4427de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase                (int) glyph.fWidth, (int) glyph.fHeight);
4437de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        return;
444694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
446694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
447694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
448694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
449694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
450694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    bool bitmapFit = false;
4517de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    CacheTextureLine *cacheLine;
452694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
453694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
454694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (bitmapFit) {
4557de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            cacheLine = mCacheLines[i];
456694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
457694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
458694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
459694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
460694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
461694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!bitmapFit) {
462694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
463694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
464694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
465694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
466694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
467694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            if (bitmapFit) {
4687de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase                cacheLine = mCacheLines[i];
469694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy                break;
470694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            }
471694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
472694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
473694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // if we still don't fit, something is wrong and we shouldn't draw
474694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (!bitmapFit) {
4757de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            return;
476694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
477694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
478694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4797de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mCachedTextureLine = cacheLine;
4807de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
481694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
482694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
483694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
484694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
485694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
486694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4877de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint32_t cacheWidth = cacheLine->mMaxWidth;
488694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4897de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    CacheTexture *cacheTexture = cacheLine->mCacheTexture;
4907de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (cacheTexture->mTexture == NULL) {
4917de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        // Large-glyph texture memory is allocated only as needed
4922a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        allocateTextureMemory(cacheTexture);
4937de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
4947de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint8_t* cacheBuffer = cacheTexture->mTexture;
49589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
496694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
497694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
498694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
499694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
500694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
50189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
502b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
503694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
504694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
5057de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mIsValid = true;
506694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
507694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5087de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
5097de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    GLuint textureId;
5107de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    glGenTextures(1, &textureId);
5112a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    uint8_t* textureMemory = NULL;
512694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5132a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
5142a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    if (allocate) {
5152a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        allocateTextureMemory(cacheTexture);
5162a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    }
5172a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    return cacheTexture;
5187de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
5197de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
5207de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasevoid FontRenderer::initTextTexture() {
5217de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.clear();
5227de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
5237de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    // Next, use other, separate caches for large glyphs.
5247de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint16_t maxWidth = 0;
5257de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (Caches::hasInstance()) {
5267de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        maxWidth = Caches::getInstance().maxTextureSize;
5277de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
5287de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
5297de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        maxWidth = MAX_TEXT_CACHE_WIDTH;
5307de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
5317de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (mCacheTextureSmall != NULL) {
5327de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTextureSmall;
5337de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTexture128;
5347de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTexture256;
5357de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        delete mCacheTexture512;
5367de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
5377de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
5387de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
5397de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
5407de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
5417de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCurrentCacheTexture = mCacheTextureSmall;
5427de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
5437de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mUploadTexture = false;
5447de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    // Split up our default cache texture into lines of certain widths
545694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nextLine = 0;
5467de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
547694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
5487de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
549694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
5507de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
55165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    nextLine += mCacheLines.top()->mMaxHeight;
5527de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
553694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
5547de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
555694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
5567de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
557694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    nextLine += mCacheLines.top()->mMaxHeight;
5587de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
5597de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            nextLine, 0, mCacheTextureSmall));
5607de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
5617de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
5627de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
5637de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
5647de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
5657de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
566694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
567694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
568694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
569694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
570d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t numIndices = mMaxNumberOfQuads * 6;
571d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
57251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
574694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
576694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
577694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
578694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
579694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
580694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
581694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
583694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
584694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
585694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
586694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
587694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
588694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
58915bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
5905d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
591694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
592694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
593694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
594d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t coordSize = 2;
595694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
596694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
5979b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
5989b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
599694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
600694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
601694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
602694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
603694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
604694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
605694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
606694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
607694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
608694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
609694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
61065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // We store a string with letters in a rough frequency of occurrence
61165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
61265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
61365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16(",.?!()-+@;:`'");
61465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    mLatinPrecache += String16("0123456789");
61565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
616694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
617694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
618694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6199b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
6207de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
6219b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
6229b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
6239b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
6242d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    Caches& caches = Caches::getInstance();
6252d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    GLuint lastTextureId = 0;
6269b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    // Iterate over all the cache lines and see which ones need to be updated
6279b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
6289b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        CacheTextureLine* cl = mCacheLines[i];
6297de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
6307de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            CacheTexture* cacheTexture = cl->mCacheTexture;
6319b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
6329b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t yOffset = cl->mCurrentRow;
6337de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            uint32_t width   = cl->mMaxWidth;
6349b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t height  = cl->mMaxHeight;
6357de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            void* textureData = cacheTexture->mTexture + (yOffset * width);
6369b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
6372d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy            if (cacheTexture->mTextureId != lastTextureId) {
6382d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                caches.activeTexture(0);
6392d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
6402d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                lastTextureId = cacheTexture->mTextureId;
6412d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy            }
6429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
6431e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
6449b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
6459b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            cl->mDirty = false;
6469b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
647694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
648694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6497de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
6502a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
6512a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
6522a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
6532a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
6542a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
6552a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    }
6567de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mLastCacheTexture = mCurrentCacheTexture;
6577de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
6589b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
6599b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
6609b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
6619b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
6629b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
6639b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
66415bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    Caches& caches = Caches::getInstance();
6652d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    caches.bindIndicesBuffer(mIndexBufferID);
66615bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    if (!mDrawn) {
66715bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        float* buffer = mTextMeshPtr;
66815bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        int offset = 2;
66915bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy
67015bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        bool force = caches.unbindMeshBuffer();
67115bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
67215bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
67315bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy                buffer + offset);
67415bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    }
67515bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy
676694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
6775b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
6785b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
679694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
680694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
681d71dd367af604571c7d00ca473184a1b9240eca2Romain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
682d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy        float x2, float y2, float u2, float v2,
683d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy        float x3, float y3, float u3, float v3,
6847de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        float x4, float y4, float u4, float v4, CacheTexture* texture) {
685d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
686ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    if (mClip &&
687ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
68809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        return;
68909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    }
6907de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (texture != mCurrentCacheTexture) {
6917de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        if (mCurrentQuadIndex != 0) {
6927de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            // First, draw everything stored already which uses the previous texture
6937de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            issueDrawCommand();
6947de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            mCurrentQuadIndex = 0;
6957de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        }
6967de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        // Now use the new texture id
6977de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mCurrentCacheTexture = texture;
6987de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
69909147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
700694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
701d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    const uint32_t floatsPerVert = 4;
70251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
703694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
704694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
705694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
706694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
707694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
708694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
709694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
710694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
711694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
712694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
713694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
714694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
715694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
716694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
717694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
718694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
719694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
720694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
721694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
722694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
723694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
724694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
725694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
7265b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
7275b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
7285b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
7295b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
7305b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
7315b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
7325b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
733694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
734694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
735694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
736694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
737694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
738694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
73965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukuint32_t FontRenderer::getRemainingCacheCapacity() {
74065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = 0;
74165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    float totalPixels = 0;
74265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
74365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
74465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk         totalPixels += mCacheLines[i]->mMaxWidth;
74565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
74665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    remainingCapacity = (remainingCapacity * 100) / totalPixels;
74765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return remainingCapacity;
74865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
74965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
75065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::precacheLatin(SkPaint* paint) {
75165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Remaining capacity is measured in %
75265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t remainingCapacity = getRemainingCacheCapacity();
75365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t precacheIdx = 0;
754054dc1840941665e32036f9523df51720ad069c8Romain Guy    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
755726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
75665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        remainingCapacity = getRemainingCacheCapacity();
75765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheIdx ++;
75865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
75965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
76065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
76165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
76265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    uint32_t currentNumFonts = mActiveFonts.size();
763325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    int flags = 0;
764325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    if (paint->isFakeBoldText()) {
765325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        flags |= Font::kFakeBold;
766325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    }
7672577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy
7682577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    const float skewX = paint->getTextSkewX();
7692577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    uint32_t italicStyle = *(uint32_t*) &skewX;
7708668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    const float scaleXFloat = paint->getTextScaleX();
7718668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
772bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    SkPaint::Style style = paint->getStyle();
773bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    const float strokeWidthFloat = paint->getStrokeWidth();
774bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
775bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
776bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy            scaleX, style, strokeWidth);
77765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
77865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    const float maxPrecacheFontSize = 40.0f;
77965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    bool isNewFont = currentNumFonts != mActiveFonts.size();
78065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
7812bffd268f135df8308c9e67af110525a5c463424Romain Guy    if (isNewFont && fontSize <= maxPrecacheFontSize) {
78265ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        precacheLatin(paint);
78365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
784694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
7857975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy
786f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
7871e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
7881e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
7891e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
7901e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
7911e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
7921e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
7931e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
7941e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
7951e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
7961e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
7971e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
7981e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
799f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
8002d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    mDrawn = false;
801ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mClip = NULL;
802ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mBounds = NULL;
803ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
804f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
805726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
806ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
8071e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
8081e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
809f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
810ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
8111e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
812f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
813f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
8141e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
815f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
816f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
817f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
818726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
8191e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy            dataBuffer, paddedWidth, paddedHeight);
820f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
821f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
822f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
823f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
824f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
825f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
826f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
827f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
8282d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy
829f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
830f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
831694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
8325b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guybool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
8335b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
834694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
835694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
83609147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    if (!mCurrentFont) {
83709147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy        LOGE("No font set");
8385b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        return false;
839694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
840694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
8415b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
8425b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
84309147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
844ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
845726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
846ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
8475b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
848ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mClip = NULL;
849694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
850694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
851694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
852694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
853694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
8545b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
8555b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
856694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
857694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
85889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
85989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
86089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
86189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
86289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
86389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
86489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
86589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
86689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
86789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
868f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
86989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
87089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
87189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
872325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    float sigma = 0.3f * (float) radius + 0.6f;
87389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
87489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
87589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
87689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
87789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
87889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
87989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
88089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
88189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
882325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
8837975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
88489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
88589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
88689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
88789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
88889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
88989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
890325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
89189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
89289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
89389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
89489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
89589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
8961e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
89789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
89889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
89989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
900325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
90189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
90289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
90389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
90489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
905325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
90689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
90789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
90889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
909325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (x > radius && x < (width - radius)) {
91089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
911325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int r = -radius; r <= radius; r ++) {
9127975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
91389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
91489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
91589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
91689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
91789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
918325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
91989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
92089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
921325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW < 0) {
92289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
92389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
924325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW > width - 1) {
92589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
92689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
92789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
928325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) input[validW];
92989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
93089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
93189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
93289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
93389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
93489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
93589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
93689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
93789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
93889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
93989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
9401e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
94189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
94289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
94389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
944325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
94589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
94689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
94789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
948325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
94989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
95089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
95189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
95289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
953325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (y > radius && y < (height - radius)) {
95489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
955325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
95689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
95789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
95889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
95989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
96089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
96189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
962325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
96389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
96489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
965325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH < 0) {
96689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
96789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
968325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH > height - 1) {
96989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
97089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
97189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
97289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
973325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) (*i);
97489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
97589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
97689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
97789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
978325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            *output = (uint8_t) blurredPixel;
97989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
98089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
98189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
98289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
98389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
98489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
98589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
98689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
98789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
988d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
98989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
990d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
99189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
99289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
993d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
99489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
99589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
99689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
99789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
998694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
999694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
1000