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