FontRenderer.cpp revision 2d4fd364843d3efc6e6ee59ccc5beb513a86d789
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    mIndexBufferID = 0;
352
353    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
354    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
355
356    char property[PROPERTY_VALUE_MAX];
357    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
358        if (sLogFontRendererCreate) {
359            INIT_LOGD("  Setting text cache width to %s pixels", property);
360        }
361        mSmallCacheWidth = atoi(property);
362    } else {
363        if (sLogFontRendererCreate) {
364            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
365        }
366    }
367
368    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
369        if (sLogFontRendererCreate) {
370            INIT_LOGD("  Setting text cache width to %s pixels", property);
371        }
372        mSmallCacheHeight = atoi(property);
373    } else {
374        if (sLogFontRendererCreate) {
375            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
376        }
377    }
378
379    sLogFontRendererCreate = false;
380}
381
382FontRenderer::~FontRenderer() {
383    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
384        delete mCacheLines[i];
385    }
386    mCacheLines.clear();
387
388    if (mInitialized) {
389        delete[] mTextMeshPtr;
390        delete mCacheTextureSmall;
391        delete mCacheTexture128;
392        delete mCacheTexture256;
393        delete mCacheTexture512;
394    }
395
396    Vector<Font*> fontsToDereference = mActiveFonts;
397    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
398        delete fontsToDereference[i];
399    }
400}
401
402void FontRenderer::flushAllAndInvalidate() {
403    if (mCurrentQuadIndex != 0) {
404        issueDrawCommand();
405        mCurrentQuadIndex = 0;
406    }
407    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
408        mActiveFonts[i]->invalidateTextureCache();
409    }
410    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
411        mCacheLines[i]->mCurrentCol = 0;
412    }
413}
414
415uint8_t* FontRenderer::allocateTextureMemory(int width, int height) {
416    uint8_t* textureMemory = new uint8_t[width * height];
417    memset(textureMemory, 0, width * height * sizeof(uint8_t));
418
419    return textureMemory;
420}
421
422void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
423        uint32_t* retOriginX, uint32_t* retOriginY) {
424    cachedGlyph->mIsValid = false;
425    // If the glyph is too tall, don't cache it
426    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
427        LOGE("Font size to large to fit in cache. width, height = %i, %i",
428                (int) glyph.fWidth, (int) glyph.fHeight);
429        return;
430    }
431
432    // Now copy the bitmap into the cache texture
433    uint32_t startX = 0;
434    uint32_t startY = 0;
435
436    bool bitmapFit = false;
437    CacheTextureLine *cacheLine;
438    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
439        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
440        if (bitmapFit) {
441            cacheLine = mCacheLines[i];
442            break;
443        }
444    }
445
446    // If the new glyph didn't fit, flush the state so far and invalidate everything
447    if (!bitmapFit) {
448        flushAllAndInvalidate();
449
450        // Try to fit it again
451        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
452            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
453            if (bitmapFit) {
454                cacheLine = mCacheLines[i];
455                break;
456            }
457        }
458
459        // if we still don't fit, something is wrong and we shouldn't draw
460        if (!bitmapFit) {
461            return;
462        }
463    }
464
465    cachedGlyph->mCachedTextureLine = cacheLine;
466
467    *retOriginX = startX;
468    *retOriginY = startY;
469
470    uint32_t endX = startX + glyph.fWidth;
471    uint32_t endY = startY + glyph.fHeight;
472
473    uint32_t cacheWidth = cacheLine->mMaxWidth;
474
475    CacheTexture *cacheTexture = cacheLine->mCacheTexture;
476    if (cacheTexture->mTexture == NULL) {
477        // Large-glyph texture memory is allocated only as needed
478        cacheTexture->mTexture = allocateTextureMemory(cacheTexture->mWidth, cacheTexture->mHeight);
479    }
480    uint8_t* cacheBuffer = cacheTexture->mTexture;
481    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
482    unsigned int stride = glyph.rowBytes();
483
484    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
485    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
486        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
487            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
488            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
489        }
490    }
491    cachedGlyph->mIsValid = true;
492}
493
494CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
495    uint8_t* textureMemory = allocate ? allocateTextureMemory(width, height) : NULL;
496    GLuint textureId;
497    glGenTextures(1, &textureId);
498    glBindTexture(GL_TEXTURE_2D, textureId);
499    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
500    // Initialize texture dimensions
501    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
502            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
503
504    mLinearFiltering = false;
505    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
506    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
507
508    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
509    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
510
511    return new CacheTexture(textureMemory, textureId, width, height);
512}
513
514void FontRenderer::initTextTexture() {
515    mCacheLines.clear();
516
517    // Next, use other, separate caches for large glyphs.
518    uint16_t maxWidth = 0;
519    if (Caches::hasInstance()) {
520        maxWidth = Caches::getInstance().maxTextureSize;
521    }
522    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
523        maxWidth = MAX_TEXT_CACHE_WIDTH;
524    }
525    if (mCacheTextureSmall != NULL) {
526        delete mCacheTextureSmall;
527        delete mCacheTexture128;
528        delete mCacheTexture256;
529        delete mCacheTexture512;
530    }
531    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
532    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
533    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
534    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
535    mCurrentCacheTexture = mCacheTextureSmall;
536
537    mUploadTexture = false;
538    // Split up our default cache texture into lines of certain widths
539    int nextLine = 0;
540    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
541    nextLine += mCacheLines.top()->mMaxHeight;
542    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
543    nextLine += mCacheLines.top()->mMaxHeight;
544    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
545    nextLine += mCacheLines.top()->mMaxHeight;
546    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
547    nextLine += mCacheLines.top()->mMaxHeight;
548    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
549    nextLine += mCacheLines.top()->mMaxHeight;
550    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
551    nextLine += mCacheLines.top()->mMaxHeight;
552    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
553            nextLine, 0, mCacheTextureSmall));
554
555    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
556    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
557    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
558    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
559    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
560}
561
562// Avoid having to reallocate memory and render quad by quad
563void FontRenderer::initVertexArrayBuffers() {
564    uint32_t numIndices = mMaxNumberOfQuads * 6;
565    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
566    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
567
568    // Four verts, two triangles , six indices per quad
569    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
570        int i6 = i * 6;
571        int i4 = i * 4;
572
573        indexBufferData[i6 + 0] = i4 + 0;
574        indexBufferData[i6 + 1] = i4 + 1;
575        indexBufferData[i6 + 2] = i4 + 2;
576
577        indexBufferData[i6 + 3] = i4 + 0;
578        indexBufferData[i6 + 4] = i4 + 2;
579        indexBufferData[i6 + 5] = i4 + 3;
580    }
581
582    glGenBuffers(1, &mIndexBufferID);
583    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
584    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
585
586    free(indexBufferData);
587
588    uint32_t coordSize = 2;
589    uint32_t uvSize = 2;
590    uint32_t vertsPerQuad = 4;
591    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
592    mTextMeshPtr = new float[vertexBufferSize];
593}
594
595// We don't want to allocate anything unless we actually draw text
596void FontRenderer::checkInit() {
597    if (mInitialized) {
598        return;
599    }
600
601    initTextTexture();
602    initVertexArrayBuffers();
603
604    // We store a string with letters in a rough frequency of occurrence
605    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
606    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
607    mLatinPrecache += String16(",.?!()-+@;:`'");
608    mLatinPrecache += String16("0123456789");
609
610    mInitialized = true;
611}
612
613void FontRenderer::checkTextureUpdate() {
614    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
615        return;
616    }
617
618    Caches& caches = Caches::getInstance();
619    GLuint lastTextureId = 0;
620    // Iterate over all the cache lines and see which ones need to be updated
621    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
622        CacheTextureLine* cl = mCacheLines[i];
623        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
624            CacheTexture* cacheTexture = cl->mCacheTexture;
625            uint32_t xOffset = 0;
626            uint32_t yOffset = cl->mCurrentRow;
627            uint32_t width   = cl->mMaxWidth;
628            uint32_t height  = cl->mMaxHeight;
629            void* textureData = cacheTexture->mTexture + (yOffset * width);
630
631            if (cacheTexture->mTextureId != lastTextureId) {
632                caches.activeTexture(0);
633                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
634                lastTextureId = cacheTexture->mTextureId;
635            }
636            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
637                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
638
639            cl->mDirty = false;
640        }
641    }
642
643    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
644    mLastCacheTexture = mCurrentCacheTexture;
645
646    mUploadTexture = false;
647}
648
649void FontRenderer::issueDrawCommand() {
650    checkTextureUpdate();
651
652    Caches& caches = Caches::getInstance();
653    caches.bindIndicesBuffer(mIndexBufferID);
654    if (!mDrawn) {
655        float* buffer = mTextMeshPtr;
656        int offset = 2;
657
658        bool force = caches.unbindMeshBuffer();
659        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
660        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
661                buffer + offset);
662    }
663
664    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
665
666    mDrawn = true;
667}
668
669void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
670        float x2, float y2, float u2, float v2,
671        float x3, float y3, float u3, float v3,
672        float x4, float y4, float u4, float v4, CacheTexture* texture) {
673
674    if (mClip &&
675            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
676        return;
677    }
678    if (texture != mCurrentCacheTexture) {
679        if (mCurrentQuadIndex != 0) {
680            // First, draw everything stored already which uses the previous texture
681            issueDrawCommand();
682            mCurrentQuadIndex = 0;
683        }
684        // Now use the new texture id
685        mCurrentCacheTexture = texture;
686    }
687
688    const uint32_t vertsPerQuad = 4;
689    const uint32_t floatsPerVert = 4;
690    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
691
692    (*currentPos++) = x1;
693    (*currentPos++) = y1;
694    (*currentPos++) = u1;
695    (*currentPos++) = v1;
696
697    (*currentPos++) = x2;
698    (*currentPos++) = y2;
699    (*currentPos++) = u2;
700    (*currentPos++) = v2;
701
702    (*currentPos++) = x3;
703    (*currentPos++) = y3;
704    (*currentPos++) = u3;
705    (*currentPos++) = v3;
706
707    (*currentPos++) = x4;
708    (*currentPos++) = y4;
709    (*currentPos++) = u4;
710    (*currentPos++) = v4;
711
712    mCurrentQuadIndex++;
713
714    if (mBounds) {
715        mBounds->left = fmin(mBounds->left, x1);
716        mBounds->top = fmin(mBounds->top, y3);
717        mBounds->right = fmax(mBounds->right, x3);
718        mBounds->bottom = fmax(mBounds->bottom, y1);
719    }
720
721    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
722        issueDrawCommand();
723        mCurrentQuadIndex = 0;
724    }
725}
726
727uint32_t FontRenderer::getRemainingCacheCapacity() {
728    uint32_t remainingCapacity = 0;
729    float totalPixels = 0;
730    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
731         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
732         totalPixels += mCacheLines[i]->mMaxWidth;
733    }
734    remainingCapacity = (remainingCapacity * 100) / totalPixels;
735    return remainingCapacity;
736}
737
738void FontRenderer::precacheLatin(SkPaint* paint) {
739    // Remaining capacity is measured in %
740    uint32_t remainingCapacity = getRemainingCacheCapacity();
741    uint32_t precacheIdx = 0;
742    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
743        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
744        remainingCapacity = getRemainingCacheCapacity();
745        precacheIdx ++;
746    }
747}
748
749void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
750    uint32_t currentNumFonts = mActiveFonts.size();
751    int flags = 0;
752    if (paint->isFakeBoldText()) {
753        flags |= Font::kFakeBold;
754    }
755
756    const float skewX = paint->getTextSkewX();
757    uint32_t italicStyle = *(uint32_t*) &skewX;
758    const float scaleXFloat = paint->getTextScaleX();
759    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
760    SkPaint::Style style = paint->getStyle();
761    const float strokeWidthFloat = paint->getStrokeWidth();
762    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
763    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
764            scaleX, style, strokeWidth);
765
766    const float maxPrecacheFontSize = 40.0f;
767    bool isNewFont = currentNumFonts != mActiveFonts.size();
768
769    if (isNewFont && fontSize <= maxPrecacheFontSize) {
770        precacheLatin(paint);
771    }
772}
773
774FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
775        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
776    checkInit();
777
778    if (!mCurrentFont) {
779        DropShadow image;
780        image.width = 0;
781        image.height = 0;
782        image.image = NULL;
783        image.penX = 0;
784        image.penY = 0;
785        return image;
786    }
787
788    mDrawn = false;
789    mClip = NULL;
790    mBounds = NULL;
791
792    Rect bounds;
793    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
794
795    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
796    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
797    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
798
799    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
800        dataBuffer[i] = 0;
801    }
802
803    int penX = radius - bounds.left;
804    int penY = radius - bounds.bottom;
805
806    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
807            dataBuffer, paddedWidth, paddedHeight);
808    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
809
810    DropShadow image;
811    image.width = paddedWidth;
812    image.height = paddedHeight;
813    image.image = dataBuffer;
814    image.penX = penX;
815    image.penY = penY;
816
817    return image;
818}
819
820bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
821        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
822    checkInit();
823
824    if (!mCurrentFont) {
825        LOGE("No font set");
826        return false;
827    }
828
829    mDrawn = false;
830    mBounds = bounds;
831    mClip = clip;
832
833    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
834
835    mBounds = NULL;
836    mClip = NULL;
837
838    if (mCurrentQuadIndex != 0) {
839        issueDrawCommand();
840        mCurrentQuadIndex = 0;
841    }
842
843    return mDrawn;
844}
845
846void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
847    // Compute gaussian weights for the blur
848    // e is the euler's number
849    float e = 2.718281828459045f;
850    float pi = 3.1415926535897932f;
851    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
852    // x is of the form [-radius .. 0 .. radius]
853    // and sigma varies with radius.
854    // Based on some experimental radius values and sigma's
855    // we approximately fit sigma = f(radius) as
856    // sigma = radius * 0.3  + 0.6
857    // The larger the radius gets, the more our gaussian blur
858    // will resemble a box blur since with large sigma
859    // the gaussian curve begins to lose its shape
860    float sigma = 0.3f * (float) radius + 0.6f;
861
862    // Now compute the coefficints
863    // We will store some redundant values to save some math during
864    // the blur calculations
865    // precompute some values
866    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
867    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
868
869    float normalizeFactor = 0.0f;
870    for (int32_t r = -radius; r <= radius; r ++) {
871        float floatR = (float) r;
872        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
873        normalizeFactor += weights[r + radius];
874    }
875
876    //Now we need to normalize the weights because all our coefficients need to add up to one
877    normalizeFactor = 1.0f / normalizeFactor;
878    for (int32_t r = -radius; r <= radius; r ++) {
879        weights[r + radius] *= normalizeFactor;
880    }
881}
882
883void FontRenderer::horizontalBlur(float* weights, int32_t radius,
884        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
885    float blurredPixel = 0.0f;
886    float currentPixel = 0.0f;
887
888    for (int32_t y = 0; y < height; y ++) {
889
890        const uint8_t* input = source + y * width;
891        uint8_t* output = dest + y * width;
892
893        for (int32_t x = 0; x < width; x ++) {
894            blurredPixel = 0.0f;
895            const float* gPtr = weights;
896            // Optimization for non-border pixels
897            if (x > radius && x < (width - radius)) {
898                const uint8_t *i = input + (x - radius);
899                for (int r = -radius; r <= radius; r ++) {
900                    currentPixel = (float) (*i);
901                    blurredPixel += currentPixel * gPtr[0];
902                    gPtr++;
903                    i++;
904                }
905            } else {
906                for (int32_t r = -radius; r <= radius; r ++) {
907                    // Stepping left and right away from the pixel
908                    int validW = x + r;
909                    if (validW < 0) {
910                        validW = 0;
911                    }
912                    if (validW > width - 1) {
913                        validW = width - 1;
914                    }
915
916                    currentPixel = (float) input[validW];
917                    blurredPixel += currentPixel * gPtr[0];
918                    gPtr++;
919                }
920            }
921            *output = (uint8_t)blurredPixel;
922            output ++;
923        }
924    }
925}
926
927void FontRenderer::verticalBlur(float* weights, int32_t radius,
928        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
929    float blurredPixel = 0.0f;
930    float currentPixel = 0.0f;
931
932    for (int32_t y = 0; y < height; y ++) {
933
934        uint8_t* output = dest + y * width;
935
936        for (int32_t x = 0; x < width; x ++) {
937            blurredPixel = 0.0f;
938            const float* gPtr = weights;
939            const uint8_t* input = source + x;
940            // Optimization for non-border pixels
941            if (y > radius && y < (height - radius)) {
942                const uint8_t *i = input + ((y - radius) * width);
943                for (int32_t r = -radius; r <= radius; r ++) {
944                    currentPixel = (float)(*i);
945                    blurredPixel += currentPixel * gPtr[0];
946                    gPtr++;
947                    i += width;
948                }
949            } else {
950                for (int32_t r = -radius; r <= radius; r ++) {
951                    int validH = y + r;
952                    // Clamp to zero and width
953                    if (validH < 0) {
954                        validH = 0;
955                    }
956                    if (validH > height - 1) {
957                        validH = height - 1;
958                    }
959
960                    const uint8_t *i = input + validH * width;
961                    currentPixel = (float) (*i);
962                    blurredPixel += currentPixel * gPtr[0];
963                    gPtr++;
964                }
965            }
966            *output = (uint8_t) blurredPixel;
967            output ++;
968        }
969    }
970}
971
972
973void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
974    float *gaussian = new float[2 * radius + 1];
975    computeGaussianWeights(gaussian, radius);
976
977    uint8_t* scratch = new uint8_t[width * height];
978
979    horizontalBlur(gaussian, radius, image, scratch, width, height);
980    verticalBlur(gaussian, radius, scratch, image, width, height);
981
982    delete[] gaussian;
983    delete[] scratch;
984}
985
986}; // namespace uirenderer
987}; // namespace android
988