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