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