FontRenderer.cpp revision b45c0c9774bd19a9dbe77d149abae4e124b08bf6
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    mGammaTable = NULL;
290    mInitialized = false;
291    mMaxNumberOfQuads = 1024;
292    mCurrentQuadIndex = 0;
293    mTextureId = 0;
294
295    mTextMeshPtr = NULL;
296    mTextTexture = NULL;
297
298    mIndexBufferID = 0;
299
300    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
301    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
302
303    char property[PROPERTY_VALUE_MAX];
304    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
305        LOGD("  Setting text cache width to %s pixels", property);
306        mCacheWidth = atoi(property);
307    } else {
308        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
309    }
310
311    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
312        LOGD("  Setting text cache width to %s pixels", property);
313        mCacheHeight = atoi(property);
314    } else {
315        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
316    }
317}
318
319FontRenderer::~FontRenderer() {
320    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
321        delete mCacheLines[i];
322    }
323    mCacheLines.clear();
324
325    if (mInitialized) {
326        delete[] mTextMeshPtr;
327        delete[] mTextTexture;
328    }
329
330    if (mTextureId) {
331        glDeleteTextures(1, &mTextureId);
332    }
333
334    Vector<Font*> fontsToDereference = mActiveFonts;
335    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
336        delete fontsToDereference[i];
337    }
338}
339
340void FontRenderer::flushAllAndInvalidate() {
341    if (mCurrentQuadIndex != 0) {
342        issueDrawCommand();
343        mCurrentQuadIndex = 0;
344    }
345    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
346        mActiveFonts[i]->invalidateTextureCache();
347    }
348    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
349        mCacheLines[i]->mCurrentCol = 0;
350    }
351}
352
353bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
354    // If the glyph is too tall, don't cache it
355    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
356        LOGE("Font size to large to fit in cache. width, height = %i, %i",
357                (int) glyph.fWidth, (int) glyph.fHeight);
358        return false;
359    }
360
361    // Now copy the bitmap into the cache texture
362    uint32_t startX = 0;
363    uint32_t startY = 0;
364
365    bool bitmapFit = false;
366    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
367        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
368        if (bitmapFit) {
369            break;
370        }
371    }
372
373    // If the new glyph didn't fit, flush the state so far and invalidate everything
374    if (!bitmapFit) {
375        flushAllAndInvalidate();
376
377        // Try to fit it again
378        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
379            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
380            if (bitmapFit) {
381                break;
382            }
383        }
384
385        // if we still don't fit, something is wrong and we shouldn't draw
386        if (!bitmapFit) {
387            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
388                    (int) glyph.fWidth, (int) glyph.fHeight);
389            return false;
390        }
391    }
392
393    *retOriginX = startX;
394    *retOriginY = startY;
395
396    uint32_t endX = startX + glyph.fWidth;
397    uint32_t endY = startY + glyph.fHeight;
398
399    uint32_t cacheWidth = mCacheWidth;
400
401    uint8_t* cacheBuffer = mTextTexture;
402    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
403    unsigned int stride = glyph.rowBytes();
404
405    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
406    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
407        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
408            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
409            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
410        }
411    }
412
413    return true;
414}
415
416void FontRenderer::initTextTexture() {
417    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
418    mUploadTexture = false;
419
420    glGenTextures(1, &mTextureId);
421    glBindTexture(GL_TEXTURE_2D, mTextureId);
422    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
423    // Initialize texture dimentions
424    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
425            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
426
427    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
428    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
429
430    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
431    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
432
433    // Split up our cache texture into lines of certain widths
434    int nextLine = 0;
435    mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
436    nextLine += mCacheLines.top()->mMaxHeight;
437    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
438    nextLine += mCacheLines.top()->mMaxHeight;
439    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
440    nextLine += mCacheLines.top()->mMaxHeight;
441    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
442    nextLine += mCacheLines.top()->mMaxHeight;
443    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
444    nextLine += mCacheLines.top()->mMaxHeight;
445    mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
446    nextLine += mCacheLines.top()->mMaxHeight;
447    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
448}
449
450// Avoid having to reallocate memory and render quad by quad
451void FontRenderer::initVertexArrayBuffers() {
452    uint32_t numIndicies = mMaxNumberOfQuads * 6;
453    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
454    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
455
456    // Four verts, two triangles , six indices per quad
457    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
458        int i6 = i * 6;
459        int i4 = i * 4;
460
461        indexBufferData[i6 + 0] = i4 + 0;
462        indexBufferData[i6 + 1] = i4 + 1;
463        indexBufferData[i6 + 2] = i4 + 2;
464
465        indexBufferData[i6 + 3] = i4 + 0;
466        indexBufferData[i6 + 4] = i4 + 2;
467        indexBufferData[i6 + 5] = i4 + 3;
468    }
469
470    glGenBuffers(1, &mIndexBufferID);
471    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
472    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
473    glBindBuffer(GL_ARRAY_BUFFER, 0);
474
475    free(indexBufferData);
476
477    uint32_t coordSize = 3;
478    uint32_t uvSize = 2;
479    uint32_t vertsPerQuad = 4;
480    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
481    mTextMeshPtr = new float[vertexBufferSize];
482}
483
484// We don't want to allocate anything unless we actually draw text
485void FontRenderer::checkInit() {
486    if (mInitialized) {
487        return;
488    }
489
490    initTextTexture();
491    initVertexArrayBuffers();
492
493    // We store a string with letters in a rough frequency of occurrence
494    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
495    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
496    mLatinPrecache += String16(",.?!()-+@;:`'");
497    mLatinPrecache += String16("0123456789");
498
499    mInitialized = true;
500}
501
502void FontRenderer::checkTextureUpdate() {
503    if (!mUploadTexture) {
504        return;
505    }
506
507    glBindTexture(GL_TEXTURE_2D, mTextureId);
508
509    // Iterate over all the cache lines and see which ones need to be updated
510    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
511        CacheTextureLine* cl = mCacheLines[i];
512        if(cl->mDirty) {
513            uint32_t xOffset = 0;
514            uint32_t yOffset = cl->mCurrentRow;
515            uint32_t width   = mCacheWidth;
516            uint32_t height  = cl->mMaxHeight;
517            void* textureData = mTextTexture + yOffset*width;
518
519            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
520                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
521
522            cl->mDirty = false;
523        }
524    }
525
526    mUploadTexture = false;
527}
528
529void FontRenderer::issueDrawCommand() {
530    checkTextureUpdate();
531
532    float* vtx = mTextMeshPtr;
533    float* tex = vtx + 3;
534
535    // position is slot 0
536    uint32_t slot = 0;
537    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
538
539    // texture0 is slot 1
540    slot = 1;
541    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
542
543    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
544    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
545}
546
547void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
548        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
549        float x4, float y4, float z4, float u4, float v4) {
550    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
551        return;
552    }
553
554    const uint32_t vertsPerQuad = 4;
555    const uint32_t floatsPerVert = 5;
556    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
557
558    (*currentPos++) = x1;
559    (*currentPos++) = y1;
560    (*currentPos++) = z1;
561    (*currentPos++) = u1;
562    (*currentPos++) = v1;
563
564    (*currentPos++) = x2;
565    (*currentPos++) = y2;
566    (*currentPos++) = z2;
567    (*currentPos++) = u2;
568    (*currentPos++) = v2;
569
570    (*currentPos++) = x3;
571    (*currentPos++) = y3;
572    (*currentPos++) = z3;
573    (*currentPos++) = u3;
574    (*currentPos++) = v3;
575
576    (*currentPos++) = x4;
577    (*currentPos++) = y4;
578    (*currentPos++) = z4;
579    (*currentPos++) = u4;
580    (*currentPos++) = v4;
581
582    mCurrentQuadIndex++;
583
584    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
585        issueDrawCommand();
586        mCurrentQuadIndex = 0;
587    }
588}
589
590uint32_t FontRenderer::getRemainingCacheCapacity() {
591    uint32_t remainingCapacity = 0;
592    float totalPixels = 0;
593    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
594         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
595         totalPixels += mCacheLines[i]->mMaxWidth;
596    }
597    remainingCapacity = (remainingCapacity * 100) / totalPixels;
598    return remainingCapacity;
599}
600
601void FontRenderer::precacheLatin(SkPaint* paint) {
602    // Remaining capacity is measured in %
603    uint32_t remainingCapacity = getRemainingCacheCapacity();
604    uint32_t precacheIdx = 0;
605    while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
606        mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
607        remainingCapacity = getRemainingCacheCapacity();
608        precacheIdx ++;
609    }
610}
611
612void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
613    uint32_t currentNumFonts = mActiveFonts.size();
614    mCurrentFont = Font::create(this, fontId, fontSize);
615
616    const float maxPrecacheFontSize = 40.0f;
617    bool isNewFont = currentNumFonts != mActiveFonts.size();
618
619    if(isNewFont && fontSize <= maxPrecacheFontSize ){
620        precacheLatin(paint);
621    }
622}
623FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
624        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
625    checkInit();
626
627    if (!mCurrentFont) {
628        DropShadow image;
629        image.width = 0;
630        image.height = 0;
631        image.image = NULL;
632        image.penX = 0;
633        image.penY = 0;
634        return image;
635    }
636
637    Rect bounds;
638    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
639    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
640    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
641    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
642    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
643        dataBuffer[i] = 0;
644    }
645
646    int penX = radius - bounds.left;
647    int penY = radius - bounds.bottom;
648
649    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
650            dataBuffer, paddedWidth, paddedHeight);
651    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
652
653    DropShadow image;
654    image.width = paddedWidth;
655    image.height = paddedHeight;
656    image.image = dataBuffer;
657    image.penX = penX;
658    image.penY = penY;
659    return image;
660}
661
662void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
663        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
664    checkInit();
665
666    if (!mCurrentFont) {
667        LOGE("No font set");
668        return;
669    }
670
671    mClip = clip;
672    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
673
674    if (mCurrentQuadIndex != 0) {
675        issueDrawCommand();
676        mCurrentQuadIndex = 0;
677    }
678}
679
680void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
681    // Compute gaussian weights for the blur
682    // e is the euler's number
683    float e = 2.718281828459045f;
684    float pi = 3.1415926535897932f;
685    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
686    // x is of the form [-radius .. 0 .. radius]
687    // and sigma varies with radius.
688    // Based on some experimental radius values and sigma's
689    // we approximately fit sigma = f(radius) as
690    // sigma = radius * 0.3  + 0.6
691    // The larger the radius gets, the more our gaussian blur
692    // will resemble a box blur since with large sigma
693    // the gaussian curve begins to lose its shape
694    float sigma = 0.3f * (float)radius + 0.6f;
695
696    // Now compute the coefficints
697    // We will store some redundant values to save some math during
698    // the blur calculations
699    // precompute some values
700    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
701    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
702
703    float normalizeFactor = 0.0f;
704    for(int32_t r = -radius; r <= radius; r ++) {
705        float floatR = (float)r;
706        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
707        normalizeFactor += weights[r + radius];
708    }
709
710    //Now we need to normalize the weights because all our coefficients need to add up to one
711    normalizeFactor = 1.0f / normalizeFactor;
712    for(int32_t r = -radius; r <= radius; r ++) {
713        weights[r + radius] *= normalizeFactor;
714    }
715}
716
717void FontRenderer::horizontalBlur(float* weights, int32_t radius,
718        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
719    float blurredPixel = 0.0f;
720    float currentPixel = 0.0f;
721
722    for(int32_t y = 0; y < height; y ++) {
723
724        const uint8_t* input = source + y * width;
725        uint8_t* output = dest + y * width;
726
727        for(int32_t x = 0; x < width; x ++) {
728            blurredPixel = 0.0f;
729            const float* gPtr = weights;
730            // Optimization for non-border pixels
731            if ((x > radius) && (x < (width - radius))) {
732                const uint8_t *i = input + (x - radius);
733                for(int r = -radius; r <= radius; r ++) {
734                    currentPixel = (float)(*i);
735                    blurredPixel += currentPixel * gPtr[0];
736                    gPtr++;
737                    i++;
738                }
739            } else {
740                for(int32_t r = -radius; r <= radius; r ++) {
741                    // Stepping left and right away from the pixel
742                    int validW = x + r;
743                    if(validW < 0) {
744                        validW = 0;
745                    }
746                    if(validW > width - 1) {
747                        validW = width - 1;
748                    }
749
750                    currentPixel = (float)(input[validW]);
751                    blurredPixel += currentPixel * gPtr[0];
752                    gPtr++;
753                }
754            }
755            *output = (uint8_t)blurredPixel;
756            output ++;
757        }
758    }
759}
760
761void FontRenderer::verticalBlur(float* weights, int32_t radius,
762        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
763    float blurredPixel = 0.0f;
764    float currentPixel = 0.0f;
765
766    for(int32_t y = 0; y < height; y ++) {
767
768        uint8_t* output = dest + y * width;
769
770        for(int32_t x = 0; x < width; x ++) {
771            blurredPixel = 0.0f;
772            const float* gPtr = weights;
773            const uint8_t* input = source + x;
774            // Optimization for non-border pixels
775            if ((y > radius) && (y < (height - radius))) {
776                const uint8_t *i = input + ((y - radius) * width);
777                for(int32_t r = -radius; r <= radius; r ++) {
778                    currentPixel = (float)(*i);
779                    blurredPixel += currentPixel * gPtr[0];
780                    gPtr++;
781                    i += width;
782                }
783            } else {
784                for(int32_t r = -radius; r <= radius; r ++) {
785                    int validH = y + r;
786                    // Clamp to zero and width
787                    if(validH < 0) {
788                        validH = 0;
789                    }
790                    if(validH > height - 1) {
791                        validH = height - 1;
792                    }
793
794                    const uint8_t *i = input + validH * width;
795                    currentPixel = (float)(*i);
796                    blurredPixel += currentPixel * gPtr[0];
797                    gPtr++;
798                }
799            }
800            *output = (uint8_t)blurredPixel;
801            output ++;
802        }
803    }
804}
805
806
807void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
808    float *gaussian = new float[2 * radius + 1];
809    computeGaussianWeights(gaussian, radius);
810    uint8_t* scratch = new uint8_t[width * height];
811    horizontalBlur(gaussian, radius, image, scratch, width, height);
812    verticalBlur(gaussian, radius, scratch, image, width, height);
813    delete[] gaussian;
814    delete[] scratch;
815}
816
817}; // namespace uirenderer
818}; // namespace android
819