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