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