FontRenderer.cpp revision 2a47c14e2a6f152496b43104bc785c488583fd59
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include <SkUtils.h>
20
21#include <cutils/properties.h>
22
23#include <utils/Log.h>
24
25#include "Caches.h"
26#include "Debug.h"
27#include "FontRenderer.h"
28#include "Caches.h"
29
30namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
34// Defines
35///////////////////////////////////////////////////////////////////////////////
36
37#define DEFAULT_TEXT_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_CACHE_HEIGHT 256
39#define MAX_TEXT_CACHE_WIDTH 2048
40#define TEXTURE_BORDER_SIZE 2
41
42///////////////////////////////////////////////////////////////////////////////
43// CacheTextureLine
44///////////////////////////////////////////////////////////////////////////////
45
46bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
47    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
48        return false;
49    }
50
51    if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
52        *retOriginX = mCurrentCol + 1;
53        *retOriginY = mCurrentRow + 1;
54        mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
55        mDirty = true;
56        return true;
57    }
58
59    return false;
60}
61
62///////////////////////////////////////////////////////////////////////////////
63// Font
64///////////////////////////////////////////////////////////////////////////////
65
66Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
67        int flags, uint32_t italicStyle, uint32_t scaleX,
68        SkPaint::Style style, uint32_t strokeWidth) :
69        mState(state), mFontId(fontId), mFontSize(fontSize),
70        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
71        mStyle(style), mStrokeWidth(mStrokeWidth) {
72}
73
74
75Font::~Font() {
76    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
77        if (mState->mActiveFonts[ct] == this) {
78            mState->mActiveFonts.removeAt(ct);
79            break;
80        }
81    }
82
83    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
84        delete mCachedGlyphs.valueAt(i);
85    }
86}
87
88void Font::invalidateTextureCache() {
89    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
90        mCachedGlyphs.valueAt(i)->mIsValid = false;
91    }
92}
93
94void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
95    int nPenX = x + glyph->mBitmapLeft;
96    int nPenY = y + glyph->mBitmapTop;
97
98    int width = (int) glyph->mBitmapWidth;
99    int height = (int) glyph->mBitmapHeight;
100
101    if (bounds->bottom > nPenY) {
102        bounds->bottom = nPenY;
103    }
104    if (bounds->left > nPenX) {
105        bounds->left = nPenX;
106    }
107    if (bounds->right < nPenX + width) {
108        bounds->right = nPenX + width;
109    }
110    if (bounds->top < nPenY + height) {
111        bounds->top = nPenY + height;
112    }
113}
114
115void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
116    int nPenX = x + glyph->mBitmapLeft;
117    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
118
119    float u1 = glyph->mBitmapMinU;
120    float u2 = glyph->mBitmapMaxU;
121    float v1 = glyph->mBitmapMinV;
122    float v2 = glyph->mBitmapMaxV;
123
124    int width = (int) glyph->mBitmapWidth;
125    int height = (int) glyph->mBitmapHeight;
126
127    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
128            nPenX + width, nPenY, u2, v2,
129            nPenX + width, nPenY - height, u2, v1,
130            nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
131}
132
133void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
134        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
135    int nPenX = x + glyph->mBitmapLeft;
136    int nPenY = y + glyph->mBitmapTop;
137
138    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
139    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
140
141    CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
142    uint32_t cacheWidth = cacheTexture->mWidth;
143    const uint8_t* cacheBuffer = cacheTexture->mTexture;
144
145    uint32_t cacheX = 0, cacheY = 0;
146    int32_t bX = 0, bY = 0;
147    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
148        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
149            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
150                LOGE("Skipping invalid index");
151                continue;
152            }
153            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
154            bitmap[bY * bitmapW + bX] = tempCol;
155        }
156    }
157}
158
159CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
160    CachedGlyphInfo* cachedGlyph = NULL;
161    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
162    if (index >= 0) {
163        cachedGlyph = mCachedGlyphs.valueAt(index);
164    } else {
165        cachedGlyph = cacheGlyph(paint, textUnit);
166    }
167
168    // Is the glyph still in texture cache?
169    if (!cachedGlyph->mIsValid) {
170        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
171        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
172    }
173
174    return cachedGlyph;
175}
176
177void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
178        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
179    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
180        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
181                bitmapW, bitmapH, NULL);
182    } else {
183        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
184                0, 0, NULL);
185    }
186}
187
188void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
189        int numGlyphs, Rect *bounds) {
190    if (bounds == NULL) {
191        LOGE("No return rectangle provided to measure text");
192        return;
193    }
194    bounds->set(1e6, -1e6, -1e6, 1e6);
195    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
196}
197
198#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
199
200void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
201        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
202        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
203    if (numGlyphs == 0 || text == NULL || len == 0) {
204        return;
205    }
206
207    float penX = x;
208    int penY = y;
209    int glyphsLeft = 1;
210    if (numGlyphs > 0) {
211        glyphsLeft = numGlyphs;
212    }
213
214    SkFixed prevRsbDelta = 0;
215    penX += 0.5f;
216
217    text += start;
218
219    while (glyphsLeft > 0) {
220        glyph_t glyph = GET_GLYPH(text);
221
222        // Reached the end of the string
223        if (IS_END_OF_STRING(glyph)) {
224            break;
225        }
226
227        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
228        penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
229        prevRsbDelta = cachedGlyph->mRsbDelta;
230
231        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
232        if (cachedGlyph->mIsValid) {
233            switch(mode) {
234            case FRAMEBUFFER:
235                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
236                break;
237            case BITMAP:
238                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
239                break;
240            case MEASURE:
241                measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
242                break;
243            }
244        }
245
246        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
247
248        // If we were given a specific number of glyphs, decrement
249        if (numGlyphs > 0) {
250            glyphsLeft--;
251        }
252    }
253}
254
255void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
256    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
257    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
258    glyph->mBitmapLeft = skiaGlyph.fLeft;
259    glyph->mBitmapTop = skiaGlyph.fTop;
260    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
261    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
262
263    uint32_t startX = 0;
264    uint32_t startY = 0;
265
266    // Get the bitmap for the glyph
267    paint->findImage(skiaGlyph);
268    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
269
270    if (!glyph->mIsValid) {
271        return;
272    }
273
274    uint32_t endX = startX + skiaGlyph.fWidth;
275    uint32_t endY = startY + skiaGlyph.fHeight;
276
277    glyph->mStartX = startX;
278    glyph->mStartY = startY;
279    glyph->mBitmapWidth = skiaGlyph.fWidth;
280    glyph->mBitmapHeight = skiaGlyph.fHeight;
281
282    uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
283    uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
284
285    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
286    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
287    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
288    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
289
290    mState->mUploadTexture = true;
291}
292
293CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
294    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
295    mCachedGlyphs.add(glyph, newGlyph);
296
297    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
298    newGlyph->mGlyphIndex = skiaGlyph.fID;
299    newGlyph->mIsValid = false;
300
301    updateGlyphCache(paint, skiaGlyph, newGlyph);
302
303    return newGlyph;
304}
305
306Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
307        int flags, uint32_t italicStyle, uint32_t scaleX,
308        SkPaint::Style style, uint32_t strokeWidth) {
309    Vector<Font*> &activeFonts = state->mActiveFonts;
310
311    for (uint32_t i = 0; i < activeFonts.size(); i++) {
312        Font* font = activeFonts[i];
313        if (font->mFontId == fontId && font->mFontSize == fontSize &&
314                font->mFlags == flags && font->mItalicStyle == italicStyle &&
315                font->mScaleX == scaleX && font->mStyle == style &&
316                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
317            return font;
318        }
319    }
320
321    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
322            scaleX, style, strokeWidth);
323    activeFonts.push(newFont);
324    return newFont;
325}
326
327///////////////////////////////////////////////////////////////////////////////
328// FontRenderer
329///////////////////////////////////////////////////////////////////////////////
330
331static bool sLogFontRendererCreate = true;
332
333FontRenderer::FontRenderer() {
334    if (sLogFontRendererCreate) {
335        INIT_LOGD("Creating FontRenderer");
336    }
337
338    mGammaTable = NULL;
339    mInitialized = false;
340    mMaxNumberOfQuads = 1024;
341    mCurrentQuadIndex = 0;
342
343    mTextMeshPtr = NULL;
344    mCurrentCacheTexture = NULL;
345    mLastCacheTexture = NULL;
346    mCacheTextureSmall = NULL;
347    mCacheTexture128 = NULL;
348    mCacheTexture256 = NULL;
349    mCacheTexture512 = NULL;
350
351    mLinearFiltering = false;
352
353    mIndexBufferID = 0;
354
355    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
356    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
357
358    char property[PROPERTY_VALUE_MAX];
359    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
360        if (sLogFontRendererCreate) {
361            INIT_LOGD("  Setting text cache width to %s pixels", property);
362        }
363        mSmallCacheWidth = atoi(property);
364    } else {
365        if (sLogFontRendererCreate) {
366            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
367        }
368    }
369
370    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
371        if (sLogFontRendererCreate) {
372            INIT_LOGD("  Setting text cache width to %s pixels", property);
373        }
374        mSmallCacheHeight = atoi(property);
375    } else {
376        if (sLogFontRendererCreate) {
377            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
378        }
379    }
380
381    sLogFontRendererCreate = false;
382}
383
384FontRenderer::~FontRenderer() {
385    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
386        delete mCacheLines[i];
387    }
388    mCacheLines.clear();
389
390    if (mInitialized) {
391        delete[] mTextMeshPtr;
392        delete mCacheTextureSmall;
393        delete mCacheTexture128;
394        delete mCacheTexture256;
395        delete mCacheTexture512;
396    }
397
398    Vector<Font*> fontsToDereference = mActiveFonts;
399    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
400        delete fontsToDereference[i];
401    }
402}
403
404void FontRenderer::flushAllAndInvalidate() {
405    if (mCurrentQuadIndex != 0) {
406        issueDrawCommand();
407        mCurrentQuadIndex = 0;
408    }
409    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
410        mActiveFonts[i]->invalidateTextureCache();
411    }
412    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
413        mCacheLines[i]->mCurrentCol = 0;
414    }
415}
416
417void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
418    int width = cacheTexture->mWidth;
419    int height = cacheTexture->mHeight;
420    cacheTexture->mTexture = new uint8_t[width * height];
421    memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
422    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
423    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
424    // Initialize texture dimensions
425    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
426            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
427
428    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
429    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
430    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
431
432    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
433    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
434}
435
436void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
437        uint32_t* retOriginX, uint32_t* retOriginY) {
438    cachedGlyph->mIsValid = false;
439    // If the glyph is too tall, don't cache it
440    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
441        LOGE("Font size to large to fit in cache. width, height = %i, %i",
442                (int) glyph.fWidth, (int) glyph.fHeight);
443        return;
444    }
445
446    // Now copy the bitmap into the cache texture
447    uint32_t startX = 0;
448    uint32_t startY = 0;
449
450    bool bitmapFit = false;
451    CacheTextureLine *cacheLine;
452    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
453        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
454        if (bitmapFit) {
455            cacheLine = mCacheLines[i];
456            break;
457        }
458    }
459
460    // If the new glyph didn't fit, flush the state so far and invalidate everything
461    if (!bitmapFit) {
462        flushAllAndInvalidate();
463
464        // Try to fit it again
465        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
466            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
467            if (bitmapFit) {
468                cacheLine = mCacheLines[i];
469                break;
470            }
471        }
472
473        // if we still don't fit, something is wrong and we shouldn't draw
474        if (!bitmapFit) {
475            return;
476        }
477    }
478
479    cachedGlyph->mCachedTextureLine = cacheLine;
480
481    *retOriginX = startX;
482    *retOriginY = startY;
483
484    uint32_t endX = startX + glyph.fWidth;
485    uint32_t endY = startY + glyph.fHeight;
486
487    uint32_t cacheWidth = cacheLine->mMaxWidth;
488
489    CacheTexture *cacheTexture = cacheLine->mCacheTexture;
490    if (cacheTexture->mTexture == NULL) {
491        // Large-glyph texture memory is allocated only as needed
492        allocateTextureMemory(cacheTexture);
493    }
494    uint8_t* cacheBuffer = cacheTexture->mTexture;
495    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
496    unsigned int stride = glyph.rowBytes();
497
498    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
499    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
500        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
501            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
502            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
503        }
504    }
505    cachedGlyph->mIsValid = true;
506}
507
508CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
509    GLuint textureId;
510    glGenTextures(1, &textureId);
511    uint8_t* textureMemory = NULL;
512
513    CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
514    if (allocate) {
515        allocateTextureMemory(cacheTexture);
516    }
517    return cacheTexture;
518}
519
520void FontRenderer::initTextTexture() {
521    mCacheLines.clear();
522
523    // Next, use other, separate caches for large glyphs.
524    uint16_t maxWidth = 0;
525    if (Caches::hasInstance()) {
526        maxWidth = Caches::getInstance().maxTextureSize;
527    }
528    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
529        maxWidth = MAX_TEXT_CACHE_WIDTH;
530    }
531    if (mCacheTextureSmall != NULL) {
532        delete mCacheTextureSmall;
533        delete mCacheTexture128;
534        delete mCacheTexture256;
535        delete mCacheTexture512;
536    }
537    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
538    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
539    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
540    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
541    mCurrentCacheTexture = mCacheTextureSmall;
542
543    mUploadTexture = false;
544    // Split up our default cache texture into lines of certain widths
545    int nextLine = 0;
546    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
547    nextLine += mCacheLines.top()->mMaxHeight;
548    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
549    nextLine += mCacheLines.top()->mMaxHeight;
550    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
551    nextLine += mCacheLines.top()->mMaxHeight;
552    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
553    nextLine += mCacheLines.top()->mMaxHeight;
554    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
555    nextLine += mCacheLines.top()->mMaxHeight;
556    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
557    nextLine += mCacheLines.top()->mMaxHeight;
558    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
559            nextLine, 0, mCacheTextureSmall));
560
561    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
562    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
563    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
564    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
565    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
566}
567
568// Avoid having to reallocate memory and render quad by quad
569void FontRenderer::initVertexArrayBuffers() {
570    uint32_t numIndices = mMaxNumberOfQuads * 6;
571    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
572    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
573
574    // Four verts, two triangles , six indices per quad
575    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
576        int i6 = i * 6;
577        int i4 = i * 4;
578
579        indexBufferData[i6 + 0] = i4 + 0;
580        indexBufferData[i6 + 1] = i4 + 1;
581        indexBufferData[i6 + 2] = i4 + 2;
582
583        indexBufferData[i6 + 3] = i4 + 0;
584        indexBufferData[i6 + 4] = i4 + 2;
585        indexBufferData[i6 + 5] = i4 + 3;
586    }
587
588    glGenBuffers(1, &mIndexBufferID);
589    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
590    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
591
592    free(indexBufferData);
593
594    uint32_t coordSize = 2;
595    uint32_t uvSize = 2;
596    uint32_t vertsPerQuad = 4;
597    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
598    mTextMeshPtr = new float[vertexBufferSize];
599}
600
601// We don't want to allocate anything unless we actually draw text
602void FontRenderer::checkInit() {
603    if (mInitialized) {
604        return;
605    }
606
607    initTextTexture();
608    initVertexArrayBuffers();
609
610    // We store a string with letters in a rough frequency of occurrence
611    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
612    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
613    mLatinPrecache += String16(",.?!()-+@;:`'");
614    mLatinPrecache += String16("0123456789");
615
616    mInitialized = true;
617}
618
619void FontRenderer::checkTextureUpdate() {
620    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
621        return;
622    }
623
624    Caches& caches = Caches::getInstance();
625    GLuint lastTextureId = 0;
626    // Iterate over all the cache lines and see which ones need to be updated
627    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
628        CacheTextureLine* cl = mCacheLines[i];
629        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
630            CacheTexture* cacheTexture = cl->mCacheTexture;
631            uint32_t xOffset = 0;
632            uint32_t yOffset = cl->mCurrentRow;
633            uint32_t width   = cl->mMaxWidth;
634            uint32_t height  = cl->mMaxHeight;
635            void* textureData = cacheTexture->mTexture + (yOffset * width);
636
637            if (cacheTexture->mTextureId != lastTextureId) {
638                caches.activeTexture(0);
639                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
640                lastTextureId = cacheTexture->mTextureId;
641            }
642            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
643                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
644
645            cl->mDirty = false;
646        }
647    }
648
649    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
650    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
651        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
652        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
653        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
654        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
655    }
656    mLastCacheTexture = mCurrentCacheTexture;
657
658    mUploadTexture = false;
659}
660
661void FontRenderer::issueDrawCommand() {
662    checkTextureUpdate();
663
664    Caches& caches = Caches::getInstance();
665    caches.bindIndicesBuffer(mIndexBufferID);
666    if (!mDrawn) {
667        float* buffer = mTextMeshPtr;
668        int offset = 2;
669
670        bool force = caches.unbindMeshBuffer();
671        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
672        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
673                buffer + offset);
674    }
675
676    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
677
678    mDrawn = true;
679}
680
681void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
682        float x2, float y2, float u2, float v2,
683        float x3, float y3, float u3, float v3,
684        float x4, float y4, float u4, float v4, CacheTexture* texture) {
685
686    if (mClip &&
687            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
688        return;
689    }
690    if (texture != mCurrentCacheTexture) {
691        if (mCurrentQuadIndex != 0) {
692            // First, draw everything stored already which uses the previous texture
693            issueDrawCommand();
694            mCurrentQuadIndex = 0;
695        }
696        // Now use the new texture id
697        mCurrentCacheTexture = texture;
698    }
699
700    const uint32_t vertsPerQuad = 4;
701    const uint32_t floatsPerVert = 4;
702    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
703
704    (*currentPos++) = x1;
705    (*currentPos++) = y1;
706    (*currentPos++) = u1;
707    (*currentPos++) = v1;
708
709    (*currentPos++) = x2;
710    (*currentPos++) = y2;
711    (*currentPos++) = u2;
712    (*currentPos++) = v2;
713
714    (*currentPos++) = x3;
715    (*currentPos++) = y3;
716    (*currentPos++) = u3;
717    (*currentPos++) = v3;
718
719    (*currentPos++) = x4;
720    (*currentPos++) = y4;
721    (*currentPos++) = u4;
722    (*currentPos++) = v4;
723
724    mCurrentQuadIndex++;
725
726    if (mBounds) {
727        mBounds->left = fmin(mBounds->left, x1);
728        mBounds->top = fmin(mBounds->top, y3);
729        mBounds->right = fmax(mBounds->right, x3);
730        mBounds->bottom = fmax(mBounds->bottom, y1);
731    }
732
733    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
734        issueDrawCommand();
735        mCurrentQuadIndex = 0;
736    }
737}
738
739uint32_t FontRenderer::getRemainingCacheCapacity() {
740    uint32_t remainingCapacity = 0;
741    float totalPixels = 0;
742    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
743         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
744         totalPixels += mCacheLines[i]->mMaxWidth;
745    }
746    remainingCapacity = (remainingCapacity * 100) / totalPixels;
747    return remainingCapacity;
748}
749
750void FontRenderer::precacheLatin(SkPaint* paint) {
751    // Remaining capacity is measured in %
752    uint32_t remainingCapacity = getRemainingCacheCapacity();
753    uint32_t precacheIdx = 0;
754    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
755        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
756        remainingCapacity = getRemainingCacheCapacity();
757        precacheIdx ++;
758    }
759}
760
761void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
762    uint32_t currentNumFonts = mActiveFonts.size();
763    int flags = 0;
764    if (paint->isFakeBoldText()) {
765        flags |= Font::kFakeBold;
766    }
767
768    const float skewX = paint->getTextSkewX();
769    uint32_t italicStyle = *(uint32_t*) &skewX;
770    const float scaleXFloat = paint->getTextScaleX();
771    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
772    SkPaint::Style style = paint->getStyle();
773    const float strokeWidthFloat = paint->getStrokeWidth();
774    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
775    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
776            scaleX, style, strokeWidth);
777
778    const float maxPrecacheFontSize = 40.0f;
779    bool isNewFont = currentNumFonts != mActiveFonts.size();
780
781    if (isNewFont && fontSize <= maxPrecacheFontSize) {
782        precacheLatin(paint);
783    }
784}
785
786FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
787        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
788    checkInit();
789
790    if (!mCurrentFont) {
791        DropShadow image;
792        image.width = 0;
793        image.height = 0;
794        image.image = NULL;
795        image.penX = 0;
796        image.penY = 0;
797        return image;
798    }
799
800    mDrawn = false;
801    mClip = NULL;
802    mBounds = NULL;
803
804    Rect bounds;
805    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
806
807    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
808    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
809    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
810
811    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
812        dataBuffer[i] = 0;
813    }
814
815    int penX = radius - bounds.left;
816    int penY = radius - bounds.bottom;
817
818    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
819            dataBuffer, paddedWidth, paddedHeight);
820    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
821
822    DropShadow image;
823    image.width = paddedWidth;
824    image.height = paddedHeight;
825    image.image = dataBuffer;
826    image.penX = penX;
827    image.penY = penY;
828
829    return image;
830}
831
832bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
833        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
834    checkInit();
835
836    if (!mCurrentFont) {
837        LOGE("No font set");
838        return false;
839    }
840
841    mDrawn = false;
842    mBounds = bounds;
843    mClip = clip;
844
845    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
846
847    mBounds = NULL;
848    mClip = NULL;
849
850    if (mCurrentQuadIndex != 0) {
851        issueDrawCommand();
852        mCurrentQuadIndex = 0;
853    }
854
855    return mDrawn;
856}
857
858void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
859    // Compute gaussian weights for the blur
860    // e is the euler's number
861    float e = 2.718281828459045f;
862    float pi = 3.1415926535897932f;
863    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
864    // x is of the form [-radius .. 0 .. radius]
865    // and sigma varies with radius.
866    // Based on some experimental radius values and sigma's
867    // we approximately fit sigma = f(radius) as
868    // sigma = radius * 0.3  + 0.6
869    // The larger the radius gets, the more our gaussian blur
870    // will resemble a box blur since with large sigma
871    // the gaussian curve begins to lose its shape
872    float sigma = 0.3f * (float) radius + 0.6f;
873
874    // Now compute the coefficints
875    // We will store some redundant values to save some math during
876    // the blur calculations
877    // precompute some values
878    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
879    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
880
881    float normalizeFactor = 0.0f;
882    for (int32_t r = -radius; r <= radius; r ++) {
883        float floatR = (float) r;
884        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
885        normalizeFactor += weights[r + radius];
886    }
887
888    //Now we need to normalize the weights because all our coefficients need to add up to one
889    normalizeFactor = 1.0f / normalizeFactor;
890    for (int32_t r = -radius; r <= radius; r ++) {
891        weights[r + radius] *= normalizeFactor;
892    }
893}
894
895void FontRenderer::horizontalBlur(float* weights, int32_t radius,
896        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
897    float blurredPixel = 0.0f;
898    float currentPixel = 0.0f;
899
900    for (int32_t y = 0; y < height; y ++) {
901
902        const uint8_t* input = source + y * width;
903        uint8_t* output = dest + y * width;
904
905        for (int32_t x = 0; x < width; x ++) {
906            blurredPixel = 0.0f;
907            const float* gPtr = weights;
908            // Optimization for non-border pixels
909            if (x > radius && x < (width - radius)) {
910                const uint8_t *i = input + (x - radius);
911                for (int r = -radius; r <= radius; r ++) {
912                    currentPixel = (float) (*i);
913                    blurredPixel += currentPixel * gPtr[0];
914                    gPtr++;
915                    i++;
916                }
917            } else {
918                for (int32_t r = -radius; r <= radius; r ++) {
919                    // Stepping left and right away from the pixel
920                    int validW = x + r;
921                    if (validW < 0) {
922                        validW = 0;
923                    }
924                    if (validW > width - 1) {
925                        validW = width - 1;
926                    }
927
928                    currentPixel = (float) input[validW];
929                    blurredPixel += currentPixel * gPtr[0];
930                    gPtr++;
931                }
932            }
933            *output = (uint8_t)blurredPixel;
934            output ++;
935        }
936    }
937}
938
939void FontRenderer::verticalBlur(float* weights, int32_t radius,
940        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
941    float blurredPixel = 0.0f;
942    float currentPixel = 0.0f;
943
944    for (int32_t y = 0; y < height; y ++) {
945
946        uint8_t* output = dest + y * width;
947
948        for (int32_t x = 0; x < width; x ++) {
949            blurredPixel = 0.0f;
950            const float* gPtr = weights;
951            const uint8_t* input = source + x;
952            // Optimization for non-border pixels
953            if (y > radius && y < (height - radius)) {
954                const uint8_t *i = input + ((y - radius) * width);
955                for (int32_t r = -radius; r <= radius; r ++) {
956                    currentPixel = (float)(*i);
957                    blurredPixel += currentPixel * gPtr[0];
958                    gPtr++;
959                    i += width;
960                }
961            } else {
962                for (int32_t r = -radius; r <= radius; r ++) {
963                    int validH = y + r;
964                    // Clamp to zero and width
965                    if (validH < 0) {
966                        validH = 0;
967                    }
968                    if (validH > height - 1) {
969                        validH = height - 1;
970                    }
971
972                    const uint8_t *i = input + validH * width;
973                    currentPixel = (float) (*i);
974                    blurredPixel += currentPixel * gPtr[0];
975                    gPtr++;
976                }
977            }
978            *output = (uint8_t) blurredPixel;
979            output ++;
980        }
981    }
982}
983
984
985void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
986    float *gaussian = new float[2 * radius + 1];
987    computeGaussianWeights(gaussian, radius);
988
989    uint8_t* scratch = new uint8_t[width * height];
990
991    horizontalBlur(gaussian, radius, image, scratch, width, height);
992    verticalBlur(gaussian, radius, scratch, image, width, height);
993
994    delete[] gaussian;
995    delete[] scratch;
996}
997
998}; // namespace uirenderer
999}; // namespace android
1000