FontRenderer.cpp revision e829bc0f0364e942bed01536d115a5c08d25d776
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    // Iterate over all the cache lines and see which ones need to be updated
619    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
620        CacheTextureLine* cl = mCacheLines[i];
621        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
622            CacheTexture* cacheTexture = cl->mCacheTexture;
623            uint32_t xOffset = 0;
624            uint32_t yOffset = cl->mCurrentRow;
625            uint32_t width   = cl->mMaxWidth;
626            uint32_t height  = cl->mMaxHeight;
627            void* textureData = cacheTexture->mTexture + (yOffset * width);
628
629            glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
630            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
631                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
632
633            cl->mDirty = false;
634        }
635    }
636
637    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
638    mLastCacheTexture = mCurrentCacheTexture;
639
640    mUploadTexture = false;
641}
642
643void FontRenderer::issueDrawCommand() {
644    checkTextureUpdate();
645
646    Caches& caches = Caches::getInstance();
647    if (!mDrawn) {
648        float* buffer = mTextMeshPtr;
649        int offset = 2;
650
651        bool force = caches.unbindMeshBuffer();
652        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
653        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
654                buffer + offset);
655    }
656
657    caches.bindIndicesBuffer(mIndexBufferID);
658    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
659
660    mDrawn = true;
661}
662
663void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
664        float x2, float y2, float u2, float v2,
665        float x3, float y3, float u3, float v3,
666        float x4, float y4, float u4, float v4, CacheTexture* texture) {
667
668    if (mClip &&
669            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
670        return;
671    }
672    if (texture != mCurrentCacheTexture) {
673        if (mCurrentQuadIndex != 0) {
674            // First, draw everything stored already which uses the previous texture
675            issueDrawCommand();
676            mCurrentQuadIndex = 0;
677        }
678        // Now use the new texture id
679        mCurrentCacheTexture = texture;
680    }
681
682    const uint32_t vertsPerQuad = 4;
683    const uint32_t floatsPerVert = 4;
684    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
685
686    (*currentPos++) = x1;
687    (*currentPos++) = y1;
688    (*currentPos++) = u1;
689    (*currentPos++) = v1;
690
691    (*currentPos++) = x2;
692    (*currentPos++) = y2;
693    (*currentPos++) = u2;
694    (*currentPos++) = v2;
695
696    (*currentPos++) = x3;
697    (*currentPos++) = y3;
698    (*currentPos++) = u3;
699    (*currentPos++) = v3;
700
701    (*currentPos++) = x4;
702    (*currentPos++) = y4;
703    (*currentPos++) = u4;
704    (*currentPos++) = v4;
705
706    mCurrentQuadIndex++;
707
708    if (mBounds) {
709        mBounds->left = fmin(mBounds->left, x1);
710        mBounds->top = fmin(mBounds->top, y3);
711        mBounds->right = fmax(mBounds->right, x3);
712        mBounds->bottom = fmax(mBounds->bottom, y1);
713    }
714
715    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
716        issueDrawCommand();
717        mCurrentQuadIndex = 0;
718    }
719}
720
721uint32_t FontRenderer::getRemainingCacheCapacity() {
722    uint32_t remainingCapacity = 0;
723    float totalPixels = 0;
724    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
725         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
726         totalPixels += mCacheLines[i]->mMaxWidth;
727    }
728    remainingCapacity = (remainingCapacity * 100) / totalPixels;
729    return remainingCapacity;
730}
731
732void FontRenderer::precacheLatin(SkPaint* paint) {
733    // Remaining capacity is measured in %
734    uint32_t remainingCapacity = getRemainingCacheCapacity();
735    uint32_t precacheIdx = 0;
736    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
737        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
738        remainingCapacity = getRemainingCacheCapacity();
739        precacheIdx ++;
740    }
741}
742
743void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
744    uint32_t currentNumFonts = mActiveFonts.size();
745    int flags = 0;
746    if (paint->isFakeBoldText()) {
747        flags |= Font::kFakeBold;
748    }
749
750    const float skewX = paint->getTextSkewX();
751    uint32_t italicStyle = *(uint32_t*) &skewX;
752    const float scaleXFloat = paint->getTextScaleX();
753    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
754    SkPaint::Style style = paint->getStyle();
755    const float strokeWidthFloat = paint->getStrokeWidth();
756    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
757    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
758            scaleX, style, strokeWidth);
759
760    const float maxPrecacheFontSize = 40.0f;
761    bool isNewFont = currentNumFonts != mActiveFonts.size();
762
763    if (isNewFont && fontSize <= maxPrecacheFontSize) {
764        precacheLatin(paint);
765    }
766}
767
768FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
769        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
770    checkInit();
771
772    if (!mCurrentFont) {
773        DropShadow image;
774        image.width = 0;
775        image.height = 0;
776        image.image = NULL;
777        image.penX = 0;
778        image.penY = 0;
779        return image;
780    }
781
782    mClip = NULL;
783    mBounds = NULL;
784
785    Rect bounds;
786    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
787
788    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
789    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
790    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
791
792    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
793        dataBuffer[i] = 0;
794    }
795
796    int penX = radius - bounds.left;
797    int penY = radius - bounds.bottom;
798
799    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
800            dataBuffer, paddedWidth, paddedHeight);
801    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
802
803    DropShadow image;
804    image.width = paddedWidth;
805    image.height = paddedHeight;
806    image.image = dataBuffer;
807    image.penX = penX;
808    image.penY = penY;
809    return image;
810}
811
812bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
813        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
814    checkInit();
815
816    if (!mCurrentFont) {
817        LOGE("No font set");
818        return false;
819    }
820
821    mDrawn = false;
822    mBounds = bounds;
823    mClip = clip;
824
825    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
826
827    mBounds = NULL;
828    mClip = NULL;
829
830    if (mCurrentQuadIndex != 0) {
831        issueDrawCommand();
832        mCurrentQuadIndex = 0;
833    }
834
835    return mDrawn;
836}
837
838void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
839    // Compute gaussian weights for the blur
840    // e is the euler's number
841    float e = 2.718281828459045f;
842    float pi = 3.1415926535897932f;
843    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
844    // x is of the form [-radius .. 0 .. radius]
845    // and sigma varies with radius.
846    // Based on some experimental radius values and sigma's
847    // we approximately fit sigma = f(radius) as
848    // sigma = radius * 0.3  + 0.6
849    // The larger the radius gets, the more our gaussian blur
850    // will resemble a box blur since with large sigma
851    // the gaussian curve begins to lose its shape
852    float sigma = 0.3f * (float) radius + 0.6f;
853
854    // Now compute the coefficints
855    // We will store some redundant values to save some math during
856    // the blur calculations
857    // precompute some values
858    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
859    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
860
861    float normalizeFactor = 0.0f;
862    for (int32_t r = -radius; r <= radius; r ++) {
863        float floatR = (float) r;
864        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
865        normalizeFactor += weights[r + radius];
866    }
867
868    //Now we need to normalize the weights because all our coefficients need to add up to one
869    normalizeFactor = 1.0f / normalizeFactor;
870    for (int32_t r = -radius; r <= radius; r ++) {
871        weights[r + radius] *= normalizeFactor;
872    }
873}
874
875void FontRenderer::horizontalBlur(float* weights, int32_t radius,
876        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
877    float blurredPixel = 0.0f;
878    float currentPixel = 0.0f;
879
880    for (int32_t y = 0; y < height; y ++) {
881
882        const uint8_t* input = source + y * width;
883        uint8_t* output = dest + y * width;
884
885        for (int32_t x = 0; x < width; x ++) {
886            blurredPixel = 0.0f;
887            const float* gPtr = weights;
888            // Optimization for non-border pixels
889            if (x > radius && x < (width - radius)) {
890                const uint8_t *i = input + (x - radius);
891                for (int r = -radius; r <= radius; r ++) {
892                    currentPixel = (float) (*i);
893                    blurredPixel += currentPixel * gPtr[0];
894                    gPtr++;
895                    i++;
896                }
897            } else {
898                for (int32_t r = -radius; r <= radius; r ++) {
899                    // Stepping left and right away from the pixel
900                    int validW = x + r;
901                    if (validW < 0) {
902                        validW = 0;
903                    }
904                    if (validW > width - 1) {
905                        validW = width - 1;
906                    }
907
908                    currentPixel = (float) input[validW];
909                    blurredPixel += currentPixel * gPtr[0];
910                    gPtr++;
911                }
912            }
913            *output = (uint8_t)blurredPixel;
914            output ++;
915        }
916    }
917}
918
919void FontRenderer::verticalBlur(float* weights, int32_t radius,
920        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
921    float blurredPixel = 0.0f;
922    float currentPixel = 0.0f;
923
924    for (int32_t y = 0; y < height; y ++) {
925
926        uint8_t* output = dest + y * width;
927
928        for (int32_t x = 0; x < width; x ++) {
929            blurredPixel = 0.0f;
930            const float* gPtr = weights;
931            const uint8_t* input = source + x;
932            // Optimization for non-border pixels
933            if (y > radius && y < (height - radius)) {
934                const uint8_t *i = input + ((y - radius) * width);
935                for (int32_t r = -radius; r <= radius; r ++) {
936                    currentPixel = (float)(*i);
937                    blurredPixel += currentPixel * gPtr[0];
938                    gPtr++;
939                    i += width;
940                }
941            } else {
942                for (int32_t r = -radius; r <= radius; r ++) {
943                    int validH = y + r;
944                    // Clamp to zero and width
945                    if (validH < 0) {
946                        validH = 0;
947                    }
948                    if (validH > height - 1) {
949                        validH = height - 1;
950                    }
951
952                    const uint8_t *i = input + validH * width;
953                    currentPixel = (float) (*i);
954                    blurredPixel += currentPixel * gPtr[0];
955                    gPtr++;
956                }
957            }
958            *output = (uint8_t) blurredPixel;
959            output ++;
960        }
961    }
962}
963
964
965void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
966    float *gaussian = new float[2 * radius + 1];
967    computeGaussianWeights(gaussian, radius);
968
969    uint8_t* scratch = new uint8_t[width * height];
970
971    horizontalBlur(gaussian, radius, image, scratch, width, height);
972    verticalBlur(gaussian, radius, scratch, image, width, height);
973
974    delete[] gaussian;
975    delete[] scratch;
976}
977
978}; // namespace uirenderer
979}; // namespace android
980