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