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