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