FontRenderer.cpp revision 378e919ccb75efe24d5a5aa75ac2c6ef255dcb48
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
40e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#define CACHE_BLOCK_ROUNDING_SIZE 4
417de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
429777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
439777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
447de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase///////////////////////////////////////////////////////////////////////////////
45e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase// CacheBlock
46e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase///////////////////////////////////////////////////////////////////////////////
47e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
48e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase/**
49e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
50e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase * order, except for the final block (the remainder space at the right, since we fill from the
51e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase * left).
52e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase */
53e816baea651476aca4407200d4a5e629b9ab8dfaChet HaaseCacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
54e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
55e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
56e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            newBlock, newBlock->mX, newBlock->mY,
57e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            newBlock->mWidth, newBlock->mHeight);
58e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
59e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    CacheBlock *currBlock = head;
60e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    CacheBlock *prevBlock = NULL;
61e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
62e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        if (newBlock->mWidth < currBlock->mWidth) {
63e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            newBlock->mNext = currBlock;
64e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            newBlock->mPrev = prevBlock;
65e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            currBlock->mPrev = newBlock;
66e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            if (prevBlock) {
67e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                prevBlock->mNext = newBlock;
68e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                return head;
69e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            } else {
70e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                return newBlock;
71e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            }
72e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        }
73e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        prevBlock = currBlock;
74e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        currBlock = currBlock->mNext;
75e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
76e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    // new block larger than all others - insert at end (but before the remainder space, if there)
77e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    newBlock->mNext = currBlock;
78e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    newBlock->mPrev = prevBlock;
79e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (currBlock) {
80e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        currBlock->mPrev = newBlock;
81e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
82e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (prevBlock) {
83e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        prevBlock->mNext = newBlock;
84e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        return head;
85e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    } else {
86e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        return newBlock;
87e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
88e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase}
89e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
90e816baea651476aca4407200d4a5e629b9ab8dfaChet HaaseCacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
91e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
92e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
93e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            blockToRemove, blockToRemove->mX, blockToRemove->mY,
94e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            blockToRemove->mWidth, blockToRemove->mHeight);
95e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
96e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    CacheBlock* newHead = head;
97e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    CacheBlock* nextBlock = blockToRemove->mNext;
98e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    CacheBlock* prevBlock = blockToRemove->mPrev;
99e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (prevBlock) {
100e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        prevBlock->mNext = nextBlock;
101e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    } else {
102e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        newHead = nextBlock;
103e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
104e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (nextBlock) {
105e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        nextBlock->mPrev = prevBlock;
106e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
107e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    delete blockToRemove;
108e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    return newHead;
109e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase}
110e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
111e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase///////////////////////////////////////////////////////////////////////////////
112378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase// CacheTexture
1137de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase///////////////////////////////////////////////////////////////////////////////
1147de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
115378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haasebool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
116378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
1177de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        return false;
1187de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
1197de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
120e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
121e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
122e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
123e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    // This columns for glyphs that are close but not necessarily exactly the same size. It trades
124e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    // off the loss of a few pixels for some glyphs against the ability to store more glyphs
125e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    // of varying sizes in one block.
126e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint16_t roundedUpW =
127e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
128e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    CacheBlock *cacheBlock = mCacheBlocks;
129e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    while (cacheBlock) {
130e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        // Store glyph in this block iff: it fits the block's remaining space and:
131e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        // it's the remainder space (mY == 0) or there's only enough height for this one glyph
132e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        // or it's within ROUNDING_SIZE of the block width
133e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
134e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
135e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                        (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
136e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            if (cacheBlock->mHeight - glyphH < glyphH) {
137e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                // Only enough space for this glyph - don't bother rounding up the width
138e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                roundedUpW = glyphW;
139e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            }
140e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            *retOriginX = cacheBlock->mX;
141378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            *retOriginY = cacheBlock->mY;
142e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            // If this is the remainder space, create a new cache block for this column. Otherwise,
143e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            // adjust the info about this column.
144e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
145e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                uint16_t oldX = cacheBlock->mX;
146e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                // Adjust remainder space dimensions
147e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                cacheBlock->mWidth -= roundedUpW;
148e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                cacheBlock->mX += roundedUpW;
149378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                if (mHeight - glyphH >= glyphH) {
150e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                    // There's enough height left over to create a new CacheBlock
151e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                    CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
152378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                            mHeight - glyphH);
153e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
154e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                    ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
155e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                            newBlock, newBlock->mX, newBlock->mY,
156e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                            newBlock->mWidth, newBlock->mHeight);
157e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
158e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                    mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
159e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                }
160e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            } else {
161e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                // Insert into current column and adjust column dimensions
162e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                cacheBlock->mY += glyphH;
163e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                cacheBlock->mHeight -= glyphH;
164e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
165e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
166e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                        cacheBlock, cacheBlock->mX, cacheBlock->mY,
167e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                        cacheBlock->mWidth, cacheBlock->mHeight);
168e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
169e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            }
170e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
171e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                // If remaining space in this block is too small to be useful, remove it
172e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase                mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
173e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            }
174e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            mDirty = true;
175e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
176e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            ALOGD("fitBitmap: current block list:");
177e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            mCacheBlocks->output();
178e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
179e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            ++mNumGlyphs;
180e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            return true;
181e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        }
182e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        cacheBlock = cacheBlock->mNext;
1837de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
184e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
185e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
186e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
1877de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    return false;
1887de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
18944984ea0cb3702384d023b5f211deda3c4b0b656Chet Haase
19051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy///////////////////////////////////////////////////////////////////////////////
191694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Font
192694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
193694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1942577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont::Font(FontRenderer* state, uint32_t fontId, float fontSize,
195bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        int flags, uint32_t italicStyle, uint32_t scaleX,
196bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        SkPaint::Style style, uint32_t strokeWidth) :
1972577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        mState(state), mFontId(fontId), mFontSize(fontSize),
198bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
199bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        mStyle(style), mStrokeWidth(mStrokeWidth) {
200694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
201694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
202694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
203694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFont::~Font() {
204694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
205694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        if (mState->mActiveFonts[ct] == this) {
206694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            mState->mActiveFonts.removeAt(ct);
207694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy            break;
208694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
209694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
210694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
211694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
212726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        delete mCachedGlyphs.valueAt(i);
213694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
214694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
215694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
216378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haasevoid Font::invalidateTextureCache(CacheTexture *cacheTexture) {
217694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
2189a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
219378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
2209a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase            cachedGlyph->mIsValid = false;
2219a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase        }
222694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
223694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
224694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
225671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
226671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
227f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
228f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
229f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
230f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int width = (int) glyph->mBitmapWidth;
231f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int height = (int) glyph->mBitmapHeight;
232f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
23361c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->bottom > nPenY) {
234f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->bottom = nPenY;
235f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
23661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->left > nPenX) {
237f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->left = nPenX;
238f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
23961c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->right < nPenX + width) {
240f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->right = nPenX + width;
241f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
24261c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds->top < nPenY + height) {
243f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        bounds->top = nPenY + height;
244f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
245f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
246f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
247671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
248671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
249694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenX = x + glyph->mBitmapLeft;
250694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
251694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
25251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u1 = glyph->mBitmapMinU;
25351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float u2 = glyph->mBitmapMaxU;
25451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v1 = glyph->mBitmapMinV;
25551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float v2 = glyph->mBitmapMaxV;
25651769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
25751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int width = (int) glyph->mBitmapWidth;
25851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    int height = (int) glyph->mBitmapHeight;
25951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
260d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
261d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy            nPenX + width, nPenY, u2, v2,
262d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy            nPenX + width, nPenY - height, u2, v1,
263378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
264694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
265694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
266671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
267671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
26889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenX = x + glyph->mBitmapLeft;
26989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    int nPenY = y + glyph->mBitmapTop;
27089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
27189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
27289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
27389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
274378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    CacheTexture *cacheTexture = glyph->mCacheTexture;
2757de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint32_t cacheWidth = cacheTexture->mWidth;
2767de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    const uint8_t* cacheBuffer = cacheTexture->mTexture;
27789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
278f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint32_t cacheX = 0, cacheY = 0;
279f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int32_t bX = 0, bY = 0;
28089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
28189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
282b629490ffb21752750cc081827ca4c1eae1eb015Romain Guy#if DEBUG_FONT_RENDERER
283b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
2843762c311729fe9f3af085c14c5c1fb471d994c03Steve Block                ALOGE("Skipping invalid index");
285f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                continue;
286f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk            }
287b629490ffb21752750cc081827ca4c1eae1eb015Romain Guy#endif
28889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
28989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            bitmap[bY * bitmapW + bX] = tempCol;
29089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
29189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
29289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
29389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
2949777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
2959777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
2969777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    const float halfWidth = glyph->mBitmapWidth * 0.5f;
2979777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    const float height = glyph->mBitmapHeight;
2989777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
2999777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    vOffset += glyph->mBitmapTop + height;
3009777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3019777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    SkPoint destination[4];
302dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy    measure.getPosTan(x + hOffset +  glyph->mBitmapLeft + halfWidth, position, tangent);
3039777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3049777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    // Move along the tangent and offset by the normal
3059777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
3069777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            -tangent->fY * halfWidth + tangent->fX * vOffset);
3079777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
3089777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            tangent->fY * halfWidth + tangent->fX * vOffset);
3099777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    destination[2].set(destination[1].fX + tangent->fY * height,
3109777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            destination[1].fY - tangent->fX * height);
3119777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    destination[3].set(destination[0].fX + tangent->fY * height,
3129777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            destination[0].fY - tangent->fX * height);
3139777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
314dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy    const float u1 = glyph->mBitmapMinU;
315dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy    const float u2 = glyph->mBitmapMaxU;
316dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy    const float v1 = glyph->mBitmapMinV;
317dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy    const float v2 = glyph->mBitmapMaxV;
318dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy
3199777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    mState->appendRotatedMeshQuad(
3209777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fX + destination[0].fX,
3219777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fY + destination[0].fY, u1, v2,
3229777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fX + destination[1].fX,
3239777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fY + destination[1].fY, u2, v2,
3249777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fX + destination[2].fX,
3259777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fY + destination[2].fY, u2, v1,
3269777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fX + destination[3].fX,
3279777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            position->fY + destination[3].fY, u1, v1,
328378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            glyph->mCacheTexture);
3299777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
3309777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3317de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
3321e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    CachedGlyphInfo* cachedGlyph = NULL;
333726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
3341e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (index >= 0) {
3351e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        cachedGlyph = mCachedGlyphs.valueAt(index);
3361e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    } else {
337726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        cachedGlyph = cacheGlyph(paint, textUnit);
33865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
33965ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
34065ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    // Is the glyph still in texture cache?
34165ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    if (!cachedGlyph->mIsValid) {
342726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
34365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
34465ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    }
34565ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
34665ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk    return cachedGlyph;
34765ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk}
34865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
349726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guyvoid Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
35061c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
35161c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
352726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
353671d6cf460531825a321edb200523d0faa7792c9Romain Guy                bitmapW, bitmapH, NULL, NULL);
35461c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    } else {
355726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
356671d6cf460531825a321edb200523d0faa7792c9Romain Guy                0, 0, NULL, NULL);
357f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
358f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
359f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
360671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
361671d6cf460531825a321edb200523d0faa7792c9Romain Guy            int numGlyphs, int x, int y, const float* positions) {
362671d6cf460531825a321edb200523d0faa7792c9Romain Guy    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
363671d6cf460531825a321edb200523d0faa7792c9Romain Guy            0, 0, NULL, positions);
364671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
365671d6cf460531825a321edb200523d0faa7792c9Romain Guy
3669777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
3679777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
3689777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
3699777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        return;
3709777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
3719777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3729777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    text += start;
3739777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3749777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    int glyphsCount = 0;
3759777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    SkFixed prevRsbDelta = 0;
3769777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3779777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    float penX = 0.0f;
3789777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3799777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    SkPoint position;
3809777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    SkVector tangent;
3819777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3829777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    SkPathMeasure measure(*path, false);
3839777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    float pathLength = SkScalarToFloat(measure.getLength());
3849777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3859777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
3869777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float textWidth = SkScalarToFloat(paint->measureText(text, len));
3879777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float pathOffset = pathLength;
3889777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
3899777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            textWidth *= 0.5f;
3909777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            pathOffset *= 0.5f;
3919777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        }
3929777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        penX += pathOffset - textWidth;
3939777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
3949777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
395dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy    while (glyphsCount < numGlyphs && penX < pathLength) {
3969777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        glyph_t glyph = GET_GLYPH(text);
3979777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
3989777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        if (IS_END_OF_STRING(glyph)) {
3999777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            break;
4009777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        }
4019777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4029777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
4039777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
4049777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        prevRsbDelta = cachedGlyph->mRsbDelta;
4059777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4069777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        if (cachedGlyph->mIsValid) {
407dd7c8e4c68205d39109d4317dd0c9b05ed43e8e5Romain Guy            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
4089777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        }
4099777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4109777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
4119777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
4129777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        glyphsCount++;
4139777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
4149777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
4159777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
416726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guyvoid Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
417416a847633680d94efb926837efdc18726d54918Raph Levien        int numGlyphs, Rect *bounds, const float* positions) {
41861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy    if (bounds == NULL) {
4193762c311729fe9f3af085c14c5c1fb471d994c03Steve Block        ALOGE("No return rectangle provided to measure text");
420f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        return;
421f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
422f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    bounds->set(1e6, -1e6, -1e6, 1e6);
423416a847633680d94efb926837efdc18726d54918Raph Levien    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
424f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
425f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
426e816baea651476aca4407200d4a5e629b9ab8dfaChet Haasevoid Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
427e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
428e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (numGlyphs == 0 || text == NULL) {
429e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        return;
430e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
431e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    int glyphsCount = 0;
432e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
433e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    while (glyphsCount < numGlyphs) {
434e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        glyph_t glyph = GET_GLYPH(text);
435e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
436e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        // Reached the end of the string
437e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        if (IS_END_OF_STRING(glyph)) {
438e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            break;
439e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        }
440e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
441e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
442e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
443e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        glyphsCount++;
444e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
445e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase}
446e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
447726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guyvoid Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
44861c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
4499777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
450694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (numGlyphs == 0 || text == NULL || len == 0) {
451694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
452694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
453694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
454671d6cf460531825a321edb200523d0faa7792c9Romain Guy    static RenderGlyph gRenderGlyph[] = {
455671d6cf460531825a321edb200523d0faa7792c9Romain Guy            &android::uirenderer::Font::drawCachedGlyph,
456671d6cf460531825a321edb200523d0faa7792c9Romain Guy            &android::uirenderer::Font::drawCachedGlyphBitmap,
457671d6cf460531825a321edb200523d0faa7792c9Romain Guy            &android::uirenderer::Font::measureCachedGlyph
458671d6cf460531825a321edb200523d0faa7792c9Romain Guy    };
459671d6cf460531825a321edb200523d0faa7792c9Romain Guy    RenderGlyph render = gRenderGlyph[mode];
460694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4619777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    text += start;
4629777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    int glyphsCount = 0;
4639777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
464b629490ffb21752750cc081827ca4c1eae1eb015Romain Guy    if (CC_LIKELY(positions == NULL)) {
465671d6cf460531825a321edb200523d0faa7792c9Romain Guy        SkFixed prevRsbDelta = 0;
466694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
4679777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float penX = x + 0.5f;
468671d6cf460531825a321edb200523d0faa7792c9Romain Guy        int penY = y;
469694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
470671d6cf460531825a321edb200523d0faa7792c9Romain Guy        while (glyphsCount < numGlyphs) {
471671d6cf460531825a321edb200523d0faa7792c9Romain Guy            glyph_t glyph = GET_GLYPH(text);
472671d6cf460531825a321edb200523d0faa7792c9Romain Guy
473671d6cf460531825a321edb200523d0faa7792c9Romain Guy            // Reached the end of the string
474671d6cf460531825a321edb200523d0faa7792c9Romain Guy            if (IS_END_OF_STRING(glyph)) {
475f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk                break;
47689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
477671d6cf460531825a321edb200523d0faa7792c9Romain Guy
478671d6cf460531825a321edb200523d0faa7792c9Romain Guy            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
4799777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
480671d6cf460531825a321edb200523d0faa7792c9Romain Guy            prevRsbDelta = cachedGlyph->mRsbDelta;
481671d6cf460531825a321edb200523d0faa7792c9Romain Guy
482671d6cf460531825a321edb200523d0faa7792c9Romain Guy            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
483671d6cf460531825a321edb200523d0faa7792c9Romain Guy            if (cachedGlyph->mIsValid) {
484671d6cf460531825a321edb200523d0faa7792c9Romain Guy                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
485671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        bitmap, bitmapW, bitmapH, bounds, positions);
486671d6cf460531825a321edb200523d0faa7792c9Romain Guy            }
487671d6cf460531825a321edb200523d0faa7792c9Romain Guy
488671d6cf460531825a321edb200523d0faa7792c9Romain Guy            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
489671d6cf460531825a321edb200523d0faa7792c9Romain Guy
490671d6cf460531825a321edb200523d0faa7792c9Romain Guy            glyphsCount++;
491694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
492671d6cf460531825a321edb200523d0faa7792c9Romain Guy    } else {
493671d6cf460531825a321edb200523d0faa7792c9Romain Guy        const SkPaint::Align align = paint->getTextAlign();
494694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
495671d6cf460531825a321edb200523d0faa7792c9Romain Guy        // This is for renderPosText()
496671d6cf460531825a321edb200523d0faa7792c9Romain Guy        while (glyphsCount < numGlyphs) {
497671d6cf460531825a321edb200523d0faa7792c9Romain Guy            glyph_t glyph = GET_GLYPH(text);
498694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
499671d6cf460531825a321edb200523d0faa7792c9Romain Guy            // Reached the end of the string
500671d6cf460531825a321edb200523d0faa7792c9Romain Guy            if (IS_END_OF_STRING(glyph)) {
501671d6cf460531825a321edb200523d0faa7792c9Romain Guy                break;
502671d6cf460531825a321edb200523d0faa7792c9Romain Guy            }
503671d6cf460531825a321edb200523d0faa7792c9Romain Guy
504671d6cf460531825a321edb200523d0faa7792c9Romain Guy            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
505671d6cf460531825a321edb200523d0faa7792c9Romain Guy
506671d6cf460531825a321edb200523d0faa7792c9Romain Guy            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
507671d6cf460531825a321edb200523d0faa7792c9Romain Guy            if (cachedGlyph->mIsValid) {
508671d6cf460531825a321edb200523d0faa7792c9Romain Guy                int penX = x + positions[(glyphsCount << 1)];
509671d6cf460531825a321edb200523d0faa7792c9Romain Guy                int penY = y + positions[(glyphsCount << 1) + 1];
510671d6cf460531825a321edb200523d0faa7792c9Romain Guy
511671d6cf460531825a321edb200523d0faa7792c9Romain Guy                switch (align) {
512671d6cf460531825a321edb200523d0faa7792c9Romain Guy                    case SkPaint::kRight_Align:
513671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
514671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
515671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        break;
516671d6cf460531825a321edb200523d0faa7792c9Romain Guy                    case SkPaint::kCenter_Align:
517671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
518671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
519671d6cf460531825a321edb200523d0faa7792c9Romain Guy                    default:
520671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        break;
521671d6cf460531825a321edb200523d0faa7792c9Romain Guy                }
522671d6cf460531825a321edb200523d0faa7792c9Romain Guy
523671d6cf460531825a321edb200523d0faa7792c9Romain Guy                (*this.*render)(cachedGlyph, penX, penY,
524671d6cf460531825a321edb200523d0faa7792c9Romain Guy                        bitmap, bitmapW, bitmapH, bounds, positions);
525671d6cf460531825a321edb200523d0faa7792c9Romain Guy            }
526671d6cf460531825a321edb200523d0faa7792c9Romain Guy
527671d6cf460531825a321edb200523d0faa7792c9Romain Guy            glyphsCount++;
528694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
529694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
530694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
531694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
53251769a68a5cb34e9564740c6a854fcb93018789dRomain Guyvoid Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
533694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
534694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
535694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapLeft = skiaGlyph.fLeft;
536694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapTop = skiaGlyph.fTop;
5372bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
5382bffd268f135df8308c9e67af110525a5c463424Romain Guy    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
539694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
540694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
541694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
542694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
543694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Get the bitmap for the glyph
544694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    paint->findImage(skiaGlyph);
5457de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
546694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
547694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (!glyph->mIsValid) {
548694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
549694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
550694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
551694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + skiaGlyph.fWidth;
552694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + skiaGlyph.fHeight;
553694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
55489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartX = startX;
55589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    glyph->mStartY = startY;
556694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapWidth = skiaGlyph.fWidth;
557694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glyph->mBitmapHeight = skiaGlyph.fHeight;
558694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
559378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
560378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
561694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
56233fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    glyph->mBitmapMinU = startX / (float) cacheWidth;
56333fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    glyph->mBitmapMinV = startY / (float) cacheHeight;
56433fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    glyph->mBitmapMaxU = endX / (float) cacheWidth;
56533fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    glyph->mBitmapMaxV = endY / (float) cacheHeight;
566694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
56751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    mState->mUploadTexture = true;
568694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
569694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5707de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
57151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
572694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCachedGlyphs.add(glyph, newGlyph);
573694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
574726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
575694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mGlyphIndex = skiaGlyph.fID;
576694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    newGlyph->mIsValid = false;
577694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
578694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    updateGlyphCache(paint, skiaGlyph, newGlyph);
579694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
580694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newGlyph;
581694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
582694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
5832577db1ec135a1470a2c42139772ec97a6c30e78Romain GuyFont* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
584bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        int flags, uint32_t italicStyle, uint32_t scaleX,
585bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy        SkPaint::Style style, uint32_t strokeWidth) {
586694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> &activeFonts = state->mActiveFonts;
587694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
588694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < activeFonts.size(); i++) {
58951769a68a5cb34e9564740c6a854fcb93018789dRomain Guy        Font* font = activeFonts[i];
5902577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy        if (font->mFontId == fontId && font->mFontSize == fontSize &&
5918668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase                font->mFlags == flags && font->mItalicStyle == italicStyle &&
592bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy                font->mScaleX == scaleX && font->mStyle == style &&
593bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
59451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy            return font;
595694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
596694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
597694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
598bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
599bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy            scaleX, style, strokeWidth);
600694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    activeFonts.push(newFont);
601694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    return newFont;
602694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
603694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
604694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
605694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// FontRenderer
606694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy///////////////////////////////////////////////////////////////////////////////
607694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
608514fb18827186591d66973c2362c859b64b63556Romain Guystatic bool sLogFontRendererCreate = true;
609514fb18827186591d66973c2362c859b64b63556Romain Guy
610694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::FontRenderer() {
611c9855a53edfac818dc68714557185977556f849dRomain Guy    if (sLogFontRendererCreate) {
612c9855a53edfac818dc68714557185977556f849dRomain Guy        INIT_LOGD("Creating FontRenderer");
613c9855a53edfac818dc68714557185977556f849dRomain Guy    }
61451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
615b45c0c9774bd19a9dbe77d149abae4e124b08bf6Romain Guy    mGammaTable = NULL;
616694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = false;
617694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mMaxNumberOfQuads = 1024;
618694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex = 0;
619694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6209cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    mTextMeshPtr = NULL;
6217de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mCurrentCacheTexture = NULL;
6227de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mLastCacheTexture = NULL;
6239cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy
6242a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    mLinearFiltering = false;
6252a47c14e2a6f152496b43104bc785c488583fd59Chet Haase
626694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mIndexBufferID = 0;
627694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6287de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
6297de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
63051769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
63151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    char property[PROPERTY_VALUE_MAX];
63251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
633c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
634c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
635c9855a53edfac818dc68714557185977556f849dRomain Guy        }
6367de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mSmallCacheWidth = atoi(property);
63751769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
638514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
6397de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
640514fb18827186591d66973c2362c859b64b63556Romain Guy        }
64151769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
64251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy
64351769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
644c9855a53edfac818dc68714557185977556f849dRomain Guy        if (sLogFontRendererCreate) {
645c9855a53edfac818dc68714557185977556f849dRomain Guy            INIT_LOGD("  Setting text cache width to %s pixels", property);
646c9855a53edfac818dc68714557185977556f849dRomain Guy        }
6477de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mSmallCacheHeight = atoi(property);
64851769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    } else {
649514fb18827186591d66973c2362c859b64b63556Romain Guy        if (sLogFontRendererCreate) {
6507de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
651514fb18827186591d66973c2362c859b64b63556Romain Guy        }
65251769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    }
653514fb18827186591d66973c2362c859b64b63556Romain Guy
654514fb18827186591d66973c2362c859b64b63556Romain Guy    sLogFontRendererCreate = false;
655694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
656694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
657694b519ac647fe998fd396fe0784cc8e179aadc4Romain GuyFontRenderer::~FontRenderer() {
658378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
659378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        delete mCacheTextures[i];
660694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
661378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.clear();
662694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
6639cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy    if (mInitialized) {
664a9dd820184ee4d083bd9b2af735dcf50b78fc6cdRomain Guy        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
665a9dd820184ee4d083bd9b2af735dcf50b78fc6cdRomain Guy        Caches::getInstance().unbindIndicesBuffer();
666b0317984d34da99b614597ad0a8b39268eacb783Romain Guy        glDeleteBuffers(1, &mIndexBufferID);
667b0317984d34da99b614597ad0a8b39268eacb783Romain Guy
6689cccc2b9bdd4850a3f9679569aaec3ab98477a5dRomain Guy        delete[] mTextMeshPtr;
6699b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
670694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
671694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    Vector<Font*> fontsToDereference = mActiveFonts;
672694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
673694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        delete fontsToDereference[i];
674694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
675694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
676694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
677694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::flushAllAndInvalidate() {
678694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
679694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
680694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
681694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
6829d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
683694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
684694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mActiveFonts[i]->invalidateTextureCache();
685694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
6869d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
687378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
688378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        mCacheTextures[i]->init();
689e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
690e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
691378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    #if DEBUG_FONT_RENDERER
692378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    uint16_t totalGlyphs = 0;
693378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
694378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        totalGlyphs += mCacheTextures[i]->mNumGlyphs;
695378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        // Erase caches, just as a debugging facility
696378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (mCacheTextures[i]->mTexture) {
697378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            memset(mCacheTextures[i]->mTexture, 0,
698378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                    mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
699378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        }
700e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
701e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
702e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
703694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
704694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
7059a8245629d69d81e0b62e52970feaf9c02580e75Chet Haasevoid FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
7069a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase    if (cacheTexture && cacheTexture->mTexture) {
7079a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase        glDeleteTextures(1, &cacheTexture->mTextureId);
7089d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy        delete[] cacheTexture->mTexture;
7099a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase        cacheTexture->mTexture = NULL;
71099a6ddd4cd8762654a575eb4ac3d0e5431d919b8Romain Guy        cacheTexture->mTextureId = 0;
7119a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase    }
7129a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase}
7139a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase
7149a8245629d69d81e0b62e52970feaf9c02580e75Chet Haasevoid FontRenderer::flushLargeCaches() {
715378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    // Start from 1; don't deallocate smallest/default texture
716378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
717378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        CacheTexture* cacheTexture = mCacheTextures[i];
718378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (cacheTexture->mTexture != NULL) {
719378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            cacheTexture->init();
720378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
721378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                mActiveFonts[j]->invalidateTextureCache(cacheTexture);
7229a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase            }
723378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            deallocateTextureMemory(cacheTexture);
7249a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase        }
7259a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase    }
7269a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase}
7279a8245629d69d81e0b62e52970feaf9c02580e75Chet Haase
7289d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guyvoid FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
7292a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    int width = cacheTexture->mWidth;
7302a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    int height = cacheTexture->mHeight;
7319d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
7322a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    cacheTexture->mTexture = new uint8_t[width * height];
73399a6ddd4cd8762654a575eb4ac3d0e5431d919b8Romain Guy
73499a6ddd4cd8762654a575eb4ac3d0e5431d919b8Romain Guy    if (!cacheTexture->mTextureId) {
73599a6ddd4cd8762654a575eb4ac3d0e5431d919b8Romain Guy        glGenTextures(1, &cacheTexture->mTextureId);
73699a6ddd4cd8762654a575eb4ac3d0e5431d919b8Romain Guy    }
7379d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
73816c88085255c71a1a8fc034129aa2dcc61e1ddd0Romain Guy    Caches::getInstance().activeTexture(0);
7392a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
7402a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
7412a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    // Initialize texture dimensions
7422a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
7432a47c14e2a6f152496b43104bc785c488583fd59Chet Haase            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
7442a47c14e2a6f152496b43104bc785c488583fd59Chet Haase
7452a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
7462a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
7472a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
7487de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
7492a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
7502a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
7517de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
7527de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
753378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet HaaseCacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
754378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        uint32_t* startX, uint32_t* startY) {
755378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
756378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
757378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            return mCacheTextures[i];
758378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        }
759378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    }
760378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    // Could not fit glyph into current cache textures
761378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    return NULL;
762378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase}
763378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase
7647de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasevoid FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
7657de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        uint32_t* retOriginX, uint32_t* retOriginY) {
7662efd5c5886d9acf747bc92f888d731ed558aabccChet Haase    checkInit();
7677de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mIsValid = false;
768694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the glyph is too tall, don't cache it
769378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
770378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
7712efd5c5886d9acf747bc92f888d731ed558aabccChet Haase        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
7722efd5c5886d9acf747bc92f888d731ed558aabccChet Haase                (int) glyph.fWidth, (int) glyph.fHeight);
7737de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        return;
774694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
775694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
776694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Now copy the bitmap into the cache texture
777694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startX = 0;
778694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t startY = 0;
779694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
780378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
781694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
782694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // If the new glyph didn't fit, flush the state so far and invalidate everything
783378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    if (!cacheTexture) {
784694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        flushAllAndInvalidate();
785694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
786694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // Try to fit it again
787378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
788694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
789694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        // if we still don't fit, something is wrong and we shouldn't draw
790378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (!cacheTexture) {
7917de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            return;
792694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
793694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
794694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
795378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    cachedGlyph->mCacheTexture = cacheTexture;
7967de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
797694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginX = startX;
798694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    *retOriginY = startY;
799694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
800694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endX = startX + glyph.fWidth;
801694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t endY = startY + glyph.fHeight;
802694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
803378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    uint32_t cacheWidth = cacheTexture->mWidth;
804694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
8059d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy    if (!cacheTexture->mTexture) {
8067de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        // Large-glyph texture memory is allocated only as needed
8072a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        allocateTextureMemory(cacheTexture);
8087de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
8099d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
8107de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint8_t* cacheBuffer = cacheTexture->mTexture;
81189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
812694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    unsigned int stride = glyph.rowBytes();
813694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
814694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
81533fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy
81633fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
81733fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
81833fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
81933fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    }
82033fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy
82133fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
82233fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
82333fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
82433fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
82533fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy    }
82633fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4Romain Guy
827b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy    if (mGammaTable) {
828b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
829b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
830b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
831b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
832b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            }
833b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy        }
834b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy    } else {
835b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
836b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
837b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
838b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
839b1d0a4ed21168fefcb82232c8f22cb95d60acb85Romain Guy            }
840694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        }
841694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
8429777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
8437de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    cachedGlyph->mIsValid = true;
844694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
845694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
8467de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet HaaseCacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
8470aa87bbfc41e8b5f52de701ac17b4e66a7a7b609Romain Guy    CacheTexture* cacheTexture = new CacheTexture(width, height);
8489d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
8492a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    if (allocate) {
8502a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        allocateTextureMemory(cacheTexture);
8512a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    }
8529d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
8532a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    return cacheTexture;
8547de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase}
8557de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
8567de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haasevoid FontRenderer::initTextTexture() {
857378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
858378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        delete mCacheTextures[i];
8599d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy    }
860378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.clear();
8619d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
8627de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    // Next, use other, separate caches for large glyphs.
8637de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    uint16_t maxWidth = 0;
8647de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (Caches::hasInstance()) {
8657de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        maxWidth = Caches::getInstance().maxTextureSize;
8667de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
8679d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
8687de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
8697de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        maxWidth = MAX_TEXT_CACHE_WIDTH;
8707de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
8719d9758ae30a59dcf594e0d26ba5d4ee153a3e44aRomain Guy
8727de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mUploadTexture = false;
873378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
874378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
875378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
876378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
877378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    mCurrentCacheTexture = mCacheTextures[0];
878694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
879694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
880694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// Avoid having to reallocate memory and render quad by quad
881694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::initVertexArrayBuffers() {
882d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t numIndices = mMaxNumberOfQuads * 6;
883d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
88451769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
885694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
886694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    // Four verts, two triangles , six indices per quad
887694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
888694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i6 = i * 6;
889694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        int i4 = i * 4;
890694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
891694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 0] = i4 + 0;
892694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 1] = i4 + 1;
893694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 2] = i4 + 2;
894694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
895694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 3] = i4 + 0;
896694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 4] = i4 + 2;
897694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        indexBufferData[i6 + 5] = i4 + 3;
898694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
899694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
900694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glGenBuffers(1, &mIndexBufferID);
90115bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
9025d794412e3e429e47404395badcd11b0b8639e8bRomain Guy    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
903694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
904694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    free(indexBufferData);
905694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
906d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    uint32_t coordSize = 2;
907694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t uvSize = 2;
908694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    uint32_t vertsPerQuad = 4;
9099b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
9109b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mTextMeshPtr = new float[vertexBufferSize];
911694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
912694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
913694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy// We don't want to allocate anything unless we actually draw text
914694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guyvoid FontRenderer::checkInit() {
915694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mInitialized) {
916694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        return;
917694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
918694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
919694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initTextTexture();
920694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    initVertexArrayBuffers();
921694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
922694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mInitialized = true;
923694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
924694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
9259b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::checkTextureUpdate() {
9267de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
9279b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        return;
9289b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    }
9299b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
9302d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    Caches& caches = Caches::getInstance();
9312d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    GLuint lastTextureId = 0;
932378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    // Iterate over all the cache textures and see which ones need to be updated
933378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
934378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        CacheTexture* cacheTexture = mCacheTextures[i];
935378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase        if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
9369b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk            uint32_t xOffset = 0;
937378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            uint32_t width   = cacheTexture->mWidth;
938378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            uint32_t height  = cacheTexture->mHeight;
939378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            void* textureData = cacheTexture->mTexture;
9409b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
9412d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy            if (cacheTexture->mTextureId != lastTextureId) {
9422d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                caches.activeTexture(0);
9432d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
9442d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy                lastTextureId = cacheTexture->mTextureId;
9452d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy            }
946e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#if DEBUG_FONT_RENDERER
947378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
948378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase                    i, xOffset, width, height);
949e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase#endif
950378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
9511e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
9529b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
953378e919ccb75efe24d5a5aa75ac2c6ef255dcb48Chet Haase            cacheTexture->mDirty = false;
9549b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk        }
955694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
956694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
95716c88085255c71a1a8fc034129aa2dcc61e1ddd0Romain Guy    caches.activeTexture(0);
9587de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
9592a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
9602a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
9612a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
9622a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
9632a47c14e2a6f152496b43104bc785c488583fd59Chet Haase        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
9642a47c14e2a6f152496b43104bc785c488583fd59Chet Haase    }
9657de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    mLastCacheTexture = mCurrentCacheTexture;
9667de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase
9679b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    mUploadTexture = false;
9689b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk}
9699b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
9709b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchoukvoid FontRenderer::issueDrawCommand() {
9719b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk    checkTextureUpdate();
9729b9902ddbb01548f4a0199087b7035e7c10b2ae7Alex Sakhartchouk
97315bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    Caches& caches = Caches::getInstance();
9742d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    caches.bindIndicesBuffer(mIndexBufferID);
97515bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    if (!mDrawn) {
97615bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        float* buffer = mTextMeshPtr;
97715bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        int offset = 2;
97815bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy
97915bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        bool force = caches.unbindMeshBuffer();
98015bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
98115bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
98215bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy                buffer + offset);
98315bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy    }
98415bc6437f8b4cf10dba55c7638d349e7b9563f4fRomain Guy
985694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
9865b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
9875b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = true;
988694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
989694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
9909777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
9919777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
9927de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        float x4, float y4, float u4, float v4, CacheTexture* texture) {
9937de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    if (texture != mCurrentCacheTexture) {
9947de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        if (mCurrentQuadIndex != 0) {
9957de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            // First, draw everything stored already which uses the previous texture
9967de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            issueDrawCommand();
9977de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase            mCurrentQuadIndex = 0;
9987de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        }
9997de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        // Now use the new texture id
10007de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase        mCurrentCacheTexture = texture;
10017de0cb12d0e5fd64811da0b5d1ae0c0d58b86f86Chet Haase    }
100209147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy
1003694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    const uint32_t vertsPerQuad = 4;
1004d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy    const uint32_t floatsPerVert = 4;
100551769a68a5cb34e9564740c6a854fcb93018789dRomain Guy    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
1006694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1007694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x1;
1008694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y1;
1009694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u1;
1010694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v1;
1011694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1012694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x2;
1013694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y2;
1014694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u2;
1015694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v2;
1016694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1017694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x3;
1018694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y3;
1019694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u3;
1020694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v3;
1021694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1022694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = x4;
1023694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = y4;
1024694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = u4;
1025694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    (*currentPos++) = v4;
1026694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1027694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    mCurrentQuadIndex++;
10289777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
10299777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
10309777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
10319777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
10329777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x4, float y4, float u4, float v4, CacheTexture* texture) {
10339777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
10349777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (mClip &&
10359777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
10369777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        return;
10379777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
10389777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
10399777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1040694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
10415b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    if (mBounds) {
10425b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->left = fmin(mBounds->left, x1);
10435b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->top = fmin(mBounds->top, y3);
10445b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->right = fmax(mBounds->right, x3);
10455b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy        mBounds->bottom = fmax(mBounds->bottom, y1);
10465b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    }
10475b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
1048694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1049694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
1050694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
1051694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
1052694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
1053694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
10549777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guyvoid FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
10559777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
10569777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float x4, float y4, float u4, float v4, CacheTexture* texture) {
10579777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
10589777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
10599777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
10609777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (mBounds) {
10619777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
10629777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
10639777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
10649777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
10659777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
10669777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
10679777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
10689777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        issueDrawCommand();
10699777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        mCurrentQuadIndex = 0;
10709777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
10719777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
10729777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
107365ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchoukvoid FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
1074325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    int flags = 0;
1075325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    if (paint->isFakeBoldText()) {
1076325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        flags |= Font::kFakeBold;
1077325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    }
10782577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy
10792577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    const float skewX = paint->getTextSkewX();
10802577db1ec135a1470a2c42139772ec97a6c30e78Romain Guy    uint32_t italicStyle = *(uint32_t*) &skewX;
10818668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    const float scaleXFloat = paint->getTextScaleX();
10828668f8a633d9299091556c3b2e5ae07be8dce360Chet Haase    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1083bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    SkPaint::Style style = paint->getStyle();
1084bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    const float strokeWidthFloat = paint->getStrokeWidth();
1085bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1086bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1087bd496bc3d481f9cfc39007d22372d3a1a8809f96Romain Guy            scaleX, style, strokeWidth);
108865ef909776c03417d8b597738da54ca211e37e4fAlex Sakhartchouk
1089694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
10907975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy
1091f18136cb3c881a9d16c1a4f0f341732c276936bfAlex SakhartchoukFontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1092416a847633680d94efb926837efdc18726d54918Raph Levien        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
10931e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    checkInit();
10941e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
10951e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    if (!mCurrentFont) {
10961e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        DropShadow image;
10971e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.width = 0;
10981e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.height = 0;
10991e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.image = NULL;
11001e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penX = 0;
11011e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        image.penY = 0;
11021e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        return image;
11031e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    }
1104f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
11052d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy    mDrawn = false;
1106ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mClip = NULL;
1107ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mBounds = NULL;
1108ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
1109f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    Rect bounds;
1110416a847633680d94efb926837efdc18726d54918Raph Levien    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1111ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
11121e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
11131e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1114f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1115ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
11161e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1117f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk        dataBuffer[i] = 0;
1118f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    }
11191e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy
1120f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penX = radius - bounds.left;
1121f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    int penY = radius - bounds.bottom;
1122f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
1123726aeba80ffc6778a9bc3e0ee957b8d644183505Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1124416a847633680d94efb926837efdc18726d54918Raph Levien            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1125f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1126f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk
1127f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    DropShadow image;
1128f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.width = paddedWidth;
1129f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.height = paddedHeight;
1130f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.image = dataBuffer;
1131f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penX = penX;
1132f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    image.penY = penY;
11332d4fd364843d3efc6e6ee59ccc5beb513a86d789Romain Guy
1134f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    return image;
1135f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk}
1136694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1137671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1138694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    checkInit();
1139694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
11405b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mDrawn = false;
11415b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = bounds;
114209147fbdc8206a0cac78bfe9083e7e15b3c5689cRomain Guy    mClip = clip;
1143671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
1144ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy
1145671d6cf460531825a321edb200523d0faa7792c9Romain Guyvoid FontRenderer::finishRender() {
11465b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    mBounds = NULL;
1147ff98fa5a847f66e591287154c634ef7895a9549cRomain Guy    mClip = NULL;
1148694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
1149694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    if (mCurrentQuadIndex != 0) {
1150694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        issueDrawCommand();
1151694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy        mCurrentQuadIndex = 0;
1152694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy    }
1153671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
1154671d6cf460531825a321edb200523d0faa7792c9Romain Guy
1155e816baea651476aca4407200d4a5e629b9ab8dfaChet Haasevoid FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1156e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    int flags = 0;
1157e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    if (paint->isFakeBoldText()) {
1158e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase        flags |= Font::kFakeBold;
1159e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    }
1160e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    const float skewX = paint->getTextSkewX();
1161e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint32_t italicStyle = *(uint32_t*) &skewX;
1162e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    const float scaleXFloat = paint->getTextScaleX();
1163e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1164e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    SkPaint::Style style = paint->getStyle();
1165e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    const float strokeWidthFloat = paint->getStrokeWidth();
1166e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1167e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    float fontSize = paint->getTextSize();
1168e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1169e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1170e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
1171e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase    font->precache(paint, text, numGlyphs);
1172e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase}
1173e816baea651476aca4407200d4a5e629b9ab8dfaChet Haase
1174671d6cf460531825a321edb200523d0faa7792c9Romain Guybool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1175671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1176671d6cf460531825a321edb200523d0faa7792c9Romain Guy    if (!mCurrentFont) {
1177671d6cf460531825a321edb200523d0faa7792c9Romain Guy        ALOGE("No font set");
1178671d6cf460531825a321edb200523d0faa7792c9Romain Guy        return false;
1179671d6cf460531825a321edb200523d0faa7792c9Romain Guy    }
1180671d6cf460531825a321edb200523d0faa7792c9Romain Guy
1181671d6cf460531825a321edb200523d0faa7792c9Romain Guy    initRender(clip, bounds);
1182671d6cf460531825a321edb200523d0faa7792c9Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1183671d6cf460531825a321edb200523d0faa7792c9Romain Guy    finishRender();
1184671d6cf460531825a321edb200523d0faa7792c9Romain Guy
1185671d6cf460531825a321edb200523d0faa7792c9Romain Guy    return mDrawn;
1186671d6cf460531825a321edb200523d0faa7792c9Romain Guy}
1187671d6cf460531825a321edb200523d0faa7792c9Romain Guy
1188671d6cf460531825a321edb200523d0faa7792c9Romain Guybool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1189671d6cf460531825a321edb200523d0faa7792c9Romain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1190671d6cf460531825a321edb200523d0faa7792c9Romain Guy        const float* positions, Rect* bounds) {
1191671d6cf460531825a321edb200523d0faa7792c9Romain Guy    if (!mCurrentFont) {
1192671d6cf460531825a321edb200523d0faa7792c9Romain Guy        ALOGE("No font set");
1193671d6cf460531825a321edb200523d0faa7792c9Romain Guy        return false;
1194671d6cf460531825a321edb200523d0faa7792c9Romain Guy    }
1195671d6cf460531825a321edb200523d0faa7792c9Romain Guy
1196671d6cf460531825a321edb200523d0faa7792c9Romain Guy    initRender(clip, bounds);
1197671d6cf460531825a321edb200523d0faa7792c9Romain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1198671d6cf460531825a321edb200523d0faa7792c9Romain Guy    finishRender();
11995b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy
12005b3b35296e8b2c8d3f07d32bb645d5414db41a1dRomain Guy    return mDrawn;
12019777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy}
12029777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
12039777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guybool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
12049777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
12059777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        float hOffset, float vOffset, Rect* bounds) {
12069777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    if (!mCurrentFont) {
12079777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        ALOGE("No font set");
12089777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy        return false;
12099777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    }
12109777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
12119777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    initRender(clip, bounds);
12129777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
12139777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    finishRender();
12149777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy
12159777173eb6c9eb97c7921c8288ebc65e3ab3ce6fRomain Guy    return mDrawn;
1216694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}
1217694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy
121889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
121989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Compute gaussian weights for the blur
122089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // e is the euler's number
122189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float e = 2.718281828459045f;
122289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float pi = 3.1415926535897932f;
122389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
122489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // x is of the form [-radius .. 0 .. radius]
122589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // and sigma varies with radius.
122689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Based on some experimental radius values and sigma's
122789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // we approximately fit sigma = f(radius) as
1228f18136cb3c881a9d16c1a4f0f341732c276936bfAlex Sakhartchouk    // sigma = radius * 0.3  + 0.6
122989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // The larger the radius gets, the more our gaussian blur
123089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // will resemble a box blur since with large sigma
123189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the gaussian curve begins to lose its shape
1232325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    float sigma = 0.3f * (float) radius + 0.6f;
123389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
123489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // Now compute the coefficints
123589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // We will store some redundant values to save some math during
123689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // the blur calculations
123789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    // precompute some values
123889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
123989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
124089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
124189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float normalizeFactor = 0.0f;
1242325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
12437975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy        float floatR = (float) r;
124489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
124589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        normalizeFactor += weights[r + radius];
124689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
124789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
124889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    //Now we need to normalize the weights because all our coefficients need to add up to one
124989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    normalizeFactor = 1.0f / normalizeFactor;
1250325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t r = -radius; r <= radius; r ++) {
125189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        weights[r + radius] *= normalizeFactor;
125289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
125389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
125489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
125589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::horizontalBlur(float* weights, int32_t radius,
12561e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
125789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
125889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
125989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1260325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
126189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
126289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        const uint8_t* input = source + y * width;
126389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
126489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1265325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
126689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
126789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
126889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
1269325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (x > radius && x < (width - radius)) {
127089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + (x - radius);
1271325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int r = -radius; r <= radius; r ++) {
12727975fb6d12cb1eb96b75e3a563627cd4c4081bd6Romain Guy                    currentPixel = (float) (*i);
127389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
127489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
127589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i++;
127689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
127789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
1278325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
127989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Stepping left and right away from the pixel
128089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validW = x + r;
1281325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW < 0) {
128289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = 0;
128389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
1284325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validW > width - 1) {
128589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validW = width - 1;
128689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
128789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1288325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) input[validW];
128989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
129089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
129189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
129289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
129389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            *output = (uint8_t)blurredPixel;
129489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
129589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
129689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
129789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
129889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
129989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::verticalBlur(float* weights, int32_t radius,
13001e45aae5de003657e5d18f74d34998f5de5db5b7Romain Guy        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
130189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float blurredPixel = 0.0f;
130289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float currentPixel = 0.0f;
130389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1304325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy    for (int32_t y = 0; y < height; y ++) {
130589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
130689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        uint8_t* output = dest + y * width;
130789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1308325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy        for (int32_t x = 0; x < width; x ++) {
130989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            blurredPixel = 0.0f;
131089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const float* gPtr = weights;
131189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            const uint8_t* input = source + x;
131289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            // Optimization for non-border pixels
1313325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            if (y > radius && y < (height - radius)) {
131489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                const uint8_t *i = input + ((y - radius) * width);
1315325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
131689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    currentPixel = (float)(*i);
131789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
131889a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
131989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    i += width;
132089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
132189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            } else {
1322325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                for (int32_t r = -radius; r <= radius; r ++) {
132389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    int validH = y + r;
132489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    // Clamp to zero and width
1325325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH < 0) {
132689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = 0;
132789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
1328325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    if (validH > height - 1) {
132989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                        validH = height - 1;
133089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    }
133189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
133289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    const uint8_t *i = input + validH * width;
1333325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy                    currentPixel = (float) (*i);
133489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    blurredPixel += currentPixel * gPtr[0];
133589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                    gPtr++;
133689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk                }
133789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            }
1338325a0f969c1d803d7e39a9caee8cc3d400350659Romain Guy            *output = (uint8_t) blurredPixel;
133989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk            output ++;
134089a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk        }
134189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    }
134289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
134389a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
134489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
134589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchoukvoid FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
134689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    float *gaussian = new float[2 * radius + 1];
134789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    computeGaussianWeights(gaussian, radius);
1348d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
134989a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    uint8_t* scratch = new uint8_t[width * height];
1350d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
135189a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    horizontalBlur(gaussian, radius, image, scratch, width, height);
135289a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    verticalBlur(gaussian, radius, scratch, image, width, height);
1353d71dd367af604571c7d00ca473184a1b9240eca2Romain Guy
135489a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] gaussian;
135589a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk    delete[] scratch;
135689a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk}
135789a524ac2d4a36739e51b01b336c0bade77e2ee0Alex Sakhartchouk
1358694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace uirenderer
1359694b519ac647fe998fd396fe0784cc8e179aadc4Romain Guy}; // namespace android
1360