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