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"
289f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy#include "Rect.h"
2951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
30694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace android {
31694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guynamespace uirenderer {
32694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
33694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
34694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
35694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
36694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
37514fb18827186591d66973c2362c859b64b63556Romain Guystatic bool sLogFontRendererCreate = true;
38514fb18827186591d66973c2362c859b64b63556Romain Guy
39694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
40c9855a53edfac818dc68714557185977556f849dRomain Guy    if (sLogFontRendererCreate) {
41c9855a53edfac818dc68714557185977556f849dRomain Guy        INIT_LOGD("Creating FontRenderer");
42c9855a53edfac818dc68714557185977556f849dRomain Guy    }
4351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
44b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
45694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
46694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
47694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
48694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
499b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    mTextMesh = NULL;
507de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCurrentCacheTexture = NULL;
517de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mLastCacheTexture = NULL;
529cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
532a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    mLinearFiltering = false;
542a47c14e2a6f152496b43104bc785c488583fd59Chet Haase
55694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
56694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
57eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
58eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
59eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
60eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
6151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
6251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
63eb32a499194119b3783b86c925172df02e5d2685Chet Haase    if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
647de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mSmallCacheWidth = atoi(property);
6551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
669f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy
67eb32a499194119b3783b86c925172df02e5d2685Chet Haase    if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
687de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mSmallCacheHeight = atoi(property);
69eb32a499194119b3783b86c925172df02e5d2685Chet Haase    }
709f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy
71eb32a499194119b3783b86c925172df02e5d2685Chet Haase    if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
72eb32a499194119b3783b86c925172df02e5d2685Chet Haase        mLargeCacheWidth = atoi(property);
73eb32a499194119b3783b86c925172df02e5d2685Chet Haase    }
749f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy
75eb32a499194119b3783b86c925172df02e5d2685Chet Haase    if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
76eb32a499194119b3783b86c925172df02e5d2685Chet Haase        mLargeCacheHeight = atoi(property);
77eb32a499194119b3783b86c925172df02e5d2685Chet Haase    }
789f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy
799f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy    uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
809f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy    mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
819f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy    mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
829f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy    mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
839f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy    mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
849f5dab3fc228fa11c32b483e6101ec086895a32bRomain Guy
85eb32a499194119b3783b86c925172df02e5d2685Chet Haase    if (sLogFontRendererCreate) {
86eb32a499194119b3783b86c925172df02e5d2685Chet Haase        INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
87eb32a499194119b3783b86c925172df02e5d2685Chet Haase                mSmallCacheWidth, mSmallCacheHeight,
88eb32a499194119b3783b86c925172df02e5d2685Chet Haase                mLargeCacheWidth, mLargeCacheHeight >> 1,
89eb32a499194119b3783b86c925172df02e5d2685Chet Haase                mLargeCacheWidth, mLargeCacheHeight >> 1,
90eb32a499194119b3783b86c925172df02e5d2685Chet Haase                mLargeCacheWidth, mLargeCacheHeight);
9151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
92514fb18827186591d66973c2362c859b64b63556Romain Guy
93514fb18827186591d66973c2362c859b64b63556Romain Guy    sLogFontRendererCreate = false;
94694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
95694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
96694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
97378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
98378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        delete mCacheTextures[i];
99694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
100378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.clear();
101694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1029cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
103a9dd820184ee4d083bd9b2af735dcf50b78fc6cdRomain Guy        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
104a9dd820184ee4d083bd9b2af735dcf50b78fc6cdRomain Guy        Caches::getInstance().unbindIndicesBuffer();
105b0317984d34da99b614597ad0a8b39268eacb783Romain Guy        glDeleteBuffers(1, &mIndexBufferID);
106b0317984d34da99b614597ad0a8b39268eacb783Romain Guy
1079b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy        delete[] mTextMesh;
1089b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
109694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
110694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
111694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
112694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
113694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
114694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
115694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
116694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
117694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
118694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
119694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
120694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
1219d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
122694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
123694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
124694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
1259d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
126378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
127378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        mCacheTextures[i]->init();
128e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
129e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
1308087246d9964b11de8ce116bc63b156faa4197e0Romain Guy#if DEBUG_FONT_RENDERER
131378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    uint16_t totalGlyphs = 0;
132378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
1338087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        totalGlyphs += mCacheTextures[i]->getGlyphCount();
134378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        // Erase caches, just as a debugging facility
1358087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        if (mCacheTextures[i]->getTexture()) {
1368087246d9964b11de8ce116bc63b156faa4197e0Romain Guy            memset(mCacheTextures[i]->getTexture(), 0,
1378087246d9964b11de8ce116bc63b156faa4197e0Romain Guy                    mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
138378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        }
139e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
140e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
141e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
142694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
143694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1449a8245629d69d81e0b62e52970feaf9c02580e75Chet Haasevoid FontRenderer::flushLargeCaches() {
145378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    // Start from 1; don't deallocate smallest/default texture
146378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
147378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        CacheTexture* cacheTexture = mCacheTextures[i];
1488087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        if (cacheTexture->getTexture()) {
149378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            cacheTexture->init();
150378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
151378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                mActiveFonts[j]->invalidateTextureCache(cacheTexture);
1529a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase            }
1538087246d9964b11de8ce116bc63b156faa4197e0Romain Guy            cacheTexture->releaseTexture();
1549a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase        }
1559a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase    }
1569a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase}
1579a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase
158378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet HaaseCacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
159378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        uint32_t* startX, uint32_t* startY) {
160378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
161378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
162378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            return mCacheTextures[i];
163378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        }
164378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    }
165378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    // Could not fit glyph into current cache textures
166378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    return NULL;
167378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase}
168378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase
1697de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasevoid FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
170f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
1712efd5c5886d9acf747bc92f888d731ed558aabccChet Haase    checkInit();
1727de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mIsValid = false;
173694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
174378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
1758087246d9964b11de8ce116bc63b156faa4197e0Romain Guy                mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
1762efd5c5886d9acf747bc92f888d731ed558aabccChet Haase        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
1772efd5c5886d9acf747bc92f888d731ed558aabccChet Haase                (int) glyph.fWidth, (int) glyph.fHeight);
1787de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        return;
179694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
180694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
181694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
182694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
183694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
184694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
185378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
186694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
187378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    if (!cacheTexture) {
188f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase        if (!precaching) {
189f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase            // If the new glyph didn't fit and we are not just trying to precache it,
190f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase            // clear out the cache and try again
191f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase            flushAllAndInvalidate();
192f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
193f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase        }
194694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
195378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (!cacheTexture) {
196f942cf10e04567f6b9456f6258e29c803b8bfb41Chet Haase            // either the glyph didn't fit or we're precaching and will cache it when we draw
1977de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            return;
198694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
199694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
200694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
201378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    cachedGlyph->mCacheTexture = cacheTexture;
2027de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
203694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
204694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
205694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
206694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
207694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
208694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2098087246d9964b11de8ce116bc63b156faa4197e0Romain Guy    uint32_t cacheWidth = cacheTexture->getWidth();
210694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2118087246d9964b11de8ce116bc63b156faa4197e0Romain Guy    if (!cacheTexture->getTexture()) {
2128087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        Caches::getInstance().activeTexture(0);
2137de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        // Large-glyph texture memory is allocated only as needed
2148087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        cacheTexture->allocateTexture();
2157de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
2169d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
2178087246d9964b11de8ce116bc63b156faa4197e0Romain Guy    uint8_t* cacheBuffer = cacheTexture->getTexture();
21889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
219694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
220694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
221694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
22233fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy
22333fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
22433fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
22533fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
22633fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    }
22733fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy
22833fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
22933fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
23033fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
23133fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
23233fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    }
23333fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy
234b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy    if (mGammaTable) {
235b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
236b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
237b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
238b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
239b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            }
240b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy        }
241b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy    } else {
242b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
243b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
244b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
245b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
246b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            }
247694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
248694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
2499777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
2507de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mIsValid = true;
251694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
252694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
2537de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
2540aa87bbfc41e8b5f52de701ac17b4e66a7a7b609Romain Guy    CacheTexture* cacheTexture = new CacheTexture(width, height);
2559d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
2562a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    if (allocate) {
2578087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        Caches::getInstance().activeTexture(0);
2588087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        cacheTexture->allocateTexture();
2592a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    }
2609d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
2612a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    return cacheTexture;
2627de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
2637de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
2647de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasevoid FontRenderer::initTextTexture() {
265378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
266378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        delete mCacheTextures[i];
2679d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy    }
268378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.clear();
2699d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
2707de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mUploadTexture = false;
271378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
272eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
273eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
274eb32a499194119b3783b86c925172df02e5d2685Chet Haase    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
275378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCurrentCacheTexture = mCacheTextures[0];
276694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
277694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
278694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
279694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
280d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t numIndices = mMaxNumberOfQuads * 6;
281d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
28251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
283694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
284694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
285694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
286694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
287694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
288694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
289694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
290694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
291694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
292694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
293694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
294694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
295694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
296694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
297694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
298694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
29915bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
3005d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
301694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
302694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
303694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
304d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t coordSize = 2;
305694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
306694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
3079b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
3089b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    mTextMesh = new float[vertexBufferSize];
309694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
310694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
311694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
312694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
313694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
314694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
315694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
316694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
317694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
318694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
319694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
320694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
321694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
322694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3239b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
3247de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
3259b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
3269b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
3279b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3282d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    Caches& caches = Caches::getInstance();
3292d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    GLuint lastTextureId = 0;
330378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    // Iterate over all the cache textures and see which ones need to be updated
331378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
332378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        CacheTexture* cacheTexture = mCacheTextures[i];
3338087246d9964b11de8ce116bc63b156faa4197e0Romain Guy        if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
334b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
335b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            // of data. So expand the dirty rect to the encompassing horizontal stripe.
336b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            const Rect* dirtyRect = cacheTexture->getDirtyRect();
337b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            uint32_t x = 0;
338b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            uint32_t y = dirtyRect->top;
3398087246d9964b11de8ce116bc63b156faa4197e0Romain Guy            uint32_t width = cacheTexture->getWidth();
340b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            uint32_t height = dirtyRect->getHeight();
341b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            void* textureData = cacheTexture->getTexture() + y * width;
3429b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3438087246d9964b11de8ce116bc63b156faa4197e0Romain Guy            if (cacheTexture->getTextureId() != lastTextureId) {
3448087246d9964b11de8ce116bc63b156faa4197e0Romain Guy                lastTextureId = cacheTexture->getTextureId();
3452d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                caches.activeTexture(0);
3468087246d9964b11de8ce116bc63b156faa4197e0Romain Guy                glBindTexture(GL_TEXTURE_2D, lastTextureId);
3472d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy            }
348e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
349b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
350b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase                    i, x, y, width, height);
351e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
352b92d8f7979c29c7c09932578a11b2f8d6eec1d90Chet Haase            glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
3531e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
3548087246d9964b11de8ce116bc63b156faa4197e0Romain Guy            cacheTexture->setDirty(false);
3559b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
356694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
357694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
35816c88085255c71a1a8fc034129aa2dcc61e1ddd0Romain Guy    caches.activeTexture(0);
3598087246d9964b11de8ce116bc63b156faa4197e0Romain Guy    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
3608087246d9964b11de8ce116bc63b156faa4197e0Romain Guy
3618087246d9964b11de8ce116bc63b156faa4197e0Romain Guy    mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
3627de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mLastCacheTexture = mCurrentCacheTexture;
3637de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
3649b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
3659b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
3669b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
3679b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
3689b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
3699b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
37015bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    Caches& caches = Caches::getInstance();
3712d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    caches.bindIndicesBuffer(mIndexBufferID);
37215bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    if (!mDrawn) {
3739b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy        float* buffer = mTextMesh;
37415bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        int offset = 2;
37515bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy
37615bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        bool force = caches.unbindMeshBuffer();
377cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        caches.bindPositionVertexPointer(force, buffer);
378cb4d6009576cf08195dc23f341a3f4939c0878bbChris Craik        caches.bindTexCoordsVertexPointer(force, buffer + offset);
37915bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    }
38015bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy
381694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
3825b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
3835b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
384694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
385694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
3869777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
3879777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
3887de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        float x4, float y4, float u4, float v4, CacheTexture* texture) {
3897de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (texture != mCurrentCacheTexture) {
3907de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        if (mCurrentQuadIndex != 0) {
3917de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            // First, draw everything stored already which uses the previous texture
3927de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            issueDrawCommand();
3937de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            mCurrentQuadIndex = 0;
3947de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        }
3957de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        // Now use the new texture id
3967de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mCurrentCacheTexture = texture;
3977de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
39809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
399694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
400d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    const uint32_t floatsPerVert = 4;
4019b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
402694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
403694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
404694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
405694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
406694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
407694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
408694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
409694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
410694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
411694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
412694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
413694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
414694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
415694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
416694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
417694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
418694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
419694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
420694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
421694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
422694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
423694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
4249777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
4259777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4269777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
4279777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
4289777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x4, float y4, float u4, float v4, CacheTexture* texture) {
4299777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4309777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (mClip &&
4319777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
4329777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        return;
4339777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
4349777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4359777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
436694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4375b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
4385b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
4395b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
4405b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
4415b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
4425b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
4435b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
444694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
445694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
446694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
447694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
448694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
449694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4509777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
4519777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
4529777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x4, float y4, float u4, float v4, CacheTexture* texture) {
4539777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4549777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
4559777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4569777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (mBounds) {
4579777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
4589777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
4599777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
4609777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
4619777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
4629777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4639777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
4649777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        issueDrawCommand();
4659777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mCurrentQuadIndex = 0;
4669777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
4679777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
4689777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
46965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
470325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    int flags = 0;
471325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    if (paint->isFakeBoldText()) {
472325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        flags |= Font::kFakeBold;
473325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    }
4742577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy
4752577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    const float skewX = paint->getTextSkewX();
4762577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    uint32_t italicStyle = *(uint32_t*) &skewX;
4778668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    const float scaleXFloat = paint->getTextScaleX();
4788668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
479bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    SkPaint::Style style = paint->getStyle();
480bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    const float strokeWidthFloat = paint->getStrokeWidth();
481bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
482bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
483bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy            scaleX, style, strokeWidth);
48465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
485694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
4867975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy
487f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
488416a847633680d94efb926837efdc18726d54918Raph Levien        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
4891e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
4901e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
4911e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
4921e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
4931e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
4941e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
4951e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
4961e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
4971e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
4981e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
4991e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
500f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
5012d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    mDrawn = false;
502ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mClip = NULL;
503ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mBounds = NULL;
504ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
505f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
506416a847633680d94efb926837efdc18726d54918Raph Levien    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
507ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
5081e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
5091e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
510f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
511ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
5121e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
513f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
514f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
5151e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
516f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
517f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
518f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
519726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
520416a847633680d94efb926837efdc18726d54918Raph Levien            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
521f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
522f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
523f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
524f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
525f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
526f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
527f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
528f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
5292d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy
530f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
531f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
532694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
533671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid FontRenderer::initRender(const Rect* clip, Rect* bounds) {
534694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
535694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5365b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
5375b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
53809147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
539671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
540ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
541671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid FontRenderer::finishRender() {
5425b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
543ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mClip = NULL;
544694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
545694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
546694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
547694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
548694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
549671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
550671d6cf460531825a321edb200523d0faa7792c9Romain Guy
551e816baea651476aca4407200d4a5e629b9ab8dfaChet Haasevoid FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
552e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    int flags = 0;
553e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (paint->isFakeBoldText()) {
554e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        flags |= Font::kFakeBold;
555e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
556e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    const float skewX = paint->getTextSkewX();
557e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint32_t italicStyle = *(uint32_t*) &skewX;
558e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    const float scaleXFloat = paint->getTextScaleX();
559e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
560e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    SkPaint::Style style = paint->getStyle();
561e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    const float strokeWidthFloat = paint->getStrokeWidth();
562e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
563e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    float fontSize = paint->getTextSize();
564e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
565e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
566e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
567e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    font->precache(paint, text, numGlyphs);
568e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase}
569e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
570671d6cf460531825a321edb200523d0faa7792c9Romain Guybool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
571671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
572671d6cf460531825a321edb200523d0faa7792c9Romain Guy    if (!mCurrentFont) {
573671d6cf460531825a321edb200523d0faa7792c9Romain Guy        ALOGE("No font set");
574671d6cf460531825a321edb200523d0faa7792c9Romain Guy        return false;
575671d6cf460531825a321edb200523d0faa7792c9Romain Guy    }
576671d6cf460531825a321edb200523d0faa7792c9Romain Guy
577671d6cf460531825a321edb200523d0faa7792c9Romain Guy    initRender(clip, bounds);
578671d6cf460531825a321edb200523d0faa7792c9Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
579671d6cf460531825a321edb200523d0faa7792c9Romain Guy    finishRender();
580671d6cf460531825a321edb200523d0faa7792c9Romain Guy
581671d6cf460531825a321edb200523d0faa7792c9Romain Guy    return mDrawn;
582671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
583671d6cf460531825a321edb200523d0faa7792c9Romain Guy
584671d6cf460531825a321edb200523d0faa7792c9Romain Guybool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
585671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
586671d6cf460531825a321edb200523d0faa7792c9Romain Guy        const float* positions, Rect* bounds) {
587671d6cf460531825a321edb200523d0faa7792c9Romain Guy    if (!mCurrentFont) {
588671d6cf460531825a321edb200523d0faa7792c9Romain Guy        ALOGE("No font set");
589671d6cf460531825a321edb200523d0faa7792c9Romain Guy        return false;
590671d6cf460531825a321edb200523d0faa7792c9Romain Guy    }
591671d6cf460531825a321edb200523d0faa7792c9Romain Guy
592671d6cf460531825a321edb200523d0faa7792c9Romain Guy    initRender(clip, bounds);
593671d6cf460531825a321edb200523d0faa7792c9Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
594671d6cf460531825a321edb200523d0faa7792c9Romain Guy    finishRender();
5955b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
5965b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
5979777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
5989777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
5999777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guybool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
6009777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
6019777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float hOffset, float vOffset, Rect* bounds) {
6029777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (!mCurrentFont) {
6039777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        ALOGE("No font set");
6049777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        return false;
6059777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
6069777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
6079777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    initRender(clip, bounds);
6089777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
6099777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    finishRender();
6109777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
6119777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    return mDrawn;
612694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
613694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6149b1204baf4740b4d443e72157dea98571cf84e1fRomain Guyvoid FontRenderer::removeFont(const Font* font) {
6159b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
6169b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy        if (mActiveFonts[ct] == font) {
6179b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy            mActiveFonts.removeAt(ct);
6189b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy            break;
6199b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy        }
6209b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    }
6219b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy
6229b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    if (mCurrentFont == font) {
6239b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy        mCurrentFont = NULL;
6249b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy    }
6259b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy}
6269b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy
62789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
62889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
62989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
63089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
63189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
63289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
63389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
63489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
63589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
63689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
637f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
63889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
63989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
64089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
641325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    float sigma = 0.3f * (float) radius + 0.6f;
64289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
64389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
64489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
64589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
64689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
64789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
64889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
64989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
65089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
651325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
6527975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
65389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
65489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
65589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
65689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
65789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
65889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
659325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
66089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
66189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
66289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
66389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
66489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
6651e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
66689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
66789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
66889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
669325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
67089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
67189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
67289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
67389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
674325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
67589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
67689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
67789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
678325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (x > radius && x < (width - radius)) {
67989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
680325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int r = -radius; r <= radius; r ++) {
6817975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
68289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
68389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
68489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
68589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
68689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
687325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
68889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
68989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
690325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW < 0) {
69189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
69289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
693325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW > width - 1) {
69489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
69589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
69689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
697325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) input[validW];
69889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
69989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
70089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
70189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
70289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
70389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
70489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
70589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
70689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
70789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
70889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
7091e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
71089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
71189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
71289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
713325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
71489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
71589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
716325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
71789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
71889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
71989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
72089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
721325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (y > radius && y < (height - radius)) {
72289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
723325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
72489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
72589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
72689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
72789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
72889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
72989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
730325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
73189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
73289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
733325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH < 0) {
73489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
73589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
736325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH > height - 1) {
73789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
73889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
73989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
74089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
741325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) (*i);
74289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
74389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
74489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
74589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
746325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            *output = (uint8_t) blurredPixel;
7479b1204baf4740b4d443e72157dea98571cf84e1fRomain Guy            output++;
74889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
74989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
75089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
75189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
75389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
75489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
75589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
756d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
75789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
758d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
75989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
76089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
761d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
76289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
76389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
76489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
76589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
766694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
767694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
768