FontRenderer.cpp revision 1de1083e98cde9bdd5e8539dbc54fdea6531906e
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    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
430
431    mUploadTexture = false;
432
433    glGenTextures(1, &mTextureId);
434    glBindTexture(GL_TEXTURE_2D, mTextureId);
435    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
436    // Initialize texture dimentions
437    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
438            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
439
440    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
441    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
442
443    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
444    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
445
446    // Split up our cache texture into lines of certain widths
447    int nextLine = 0;
448    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
449    nextLine += mCacheLines.top()->mMaxHeight;
450    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
451    nextLine += mCacheLines.top()->mMaxHeight;
452    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
453    nextLine += mCacheLines.top()->mMaxHeight;
454    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
455    nextLine += mCacheLines.top()->mMaxHeight;
456    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
457    nextLine += mCacheLines.top()->mMaxHeight;
458    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
459    nextLine += mCacheLines.top()->mMaxHeight;
460    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
461}
462
463// Avoid having to reallocate memory and render quad by quad
464void FontRenderer::initVertexArrayBuffers() {
465    uint32_t numIndicies = mMaxNumberOfQuads * 6;
466    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
467    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
468
469    // Four verts, two triangles , six indices per quad
470    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
471        int i6 = i * 6;
472        int i4 = i * 4;
473
474        indexBufferData[i6 + 0] = i4 + 0;
475        indexBufferData[i6 + 1] = i4 + 1;
476        indexBufferData[i6 + 2] = i4 + 2;
477
478        indexBufferData[i6 + 3] = i4 + 0;
479        indexBufferData[i6 + 4] = i4 + 2;
480        indexBufferData[i6 + 5] = i4 + 3;
481    }
482
483    glGenBuffers(1, &mIndexBufferID);
484    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
485    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
486    glBindBuffer(GL_ARRAY_BUFFER, 0);
487
488    free(indexBufferData);
489
490    uint32_t coordSize = 3;
491    uint32_t uvSize = 2;
492    uint32_t vertsPerQuad = 4;
493    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
494    mTextMeshPtr = new float[vertexBufferSize];
495}
496
497// We don't want to allocate anything unless we actually draw text
498void FontRenderer::checkInit() {
499    if (mInitialized) {
500        return;
501    }
502
503    initTextTexture();
504    initVertexArrayBuffers();
505
506    // We store a string with letters in a rough frequency of occurrence
507    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
508    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
509    mLatinPrecache += String16(",.?!()-+@;:`'");
510    mLatinPrecache += String16("0123456789");
511
512    mInitialized = true;
513}
514
515void FontRenderer::checkTextureUpdate() {
516    if (!mUploadTexture) {
517        return;
518    }
519
520    glBindTexture(GL_TEXTURE_2D, mTextureId);
521
522    // Iterate over all the cache lines and see which ones need to be updated
523    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
524        CacheTextureLine* cl = mCacheLines[i];
525        if(cl->mDirty) {
526            uint32_t xOffset = 0;
527            uint32_t yOffset = cl->mCurrentRow;
528            uint32_t width   = mCacheWidth;
529            uint32_t height  = cl->mMaxHeight;
530            void* textureData = mTextTexture + yOffset*width;
531
532            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
533                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
534
535            cl->mDirty = false;
536        }
537    }
538
539    mUploadTexture = false;
540}
541
542void FontRenderer::issueDrawCommand() {
543    checkTextureUpdate();
544
545    float* vtx = mTextMeshPtr;
546    float* tex = vtx + 3;
547
548    // position is slot 0
549    uint32_t slot = 0;
550    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
551
552    // texture0 is slot 1
553    slot = 1;
554    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
555
556    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
557    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
558}
559
560void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
561        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
562        float x4, float y4, float z4, float u4, float v4) {
563    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
564        return;
565    }
566
567    const uint32_t vertsPerQuad = 4;
568    const uint32_t floatsPerVert = 5;
569    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
570
571    (*currentPos++) = x1;
572    (*currentPos++) = y1;
573    (*currentPos++) = z1;
574    (*currentPos++) = u1;
575    (*currentPos++) = v1;
576
577    (*currentPos++) = x2;
578    (*currentPos++) = y2;
579    (*currentPos++) = z2;
580    (*currentPos++) = u2;
581    (*currentPos++) = v2;
582
583    (*currentPos++) = x3;
584    (*currentPos++) = y3;
585    (*currentPos++) = z3;
586    (*currentPos++) = u3;
587    (*currentPos++) = v3;
588
589    (*currentPos++) = x4;
590    (*currentPos++) = y4;
591    (*currentPos++) = z4;
592    (*currentPos++) = u4;
593    (*currentPos++) = v4;
594
595    mCurrentQuadIndex++;
596
597    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
598        issueDrawCommand();
599        mCurrentQuadIndex = 0;
600    }
601}
602
603uint32_t FontRenderer::getRemainingCacheCapacity() {
604    uint32_t remainingCapacity = 0;
605    float totalPixels = 0;
606    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
607         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
608         totalPixels += mCacheLines[i]->mMaxWidth;
609    }
610    remainingCapacity = (remainingCapacity * 100) / totalPixels;
611    return remainingCapacity;
612}
613
614void FontRenderer::precacheLatin(SkPaint* paint) {
615    // Remaining capacity is measured in %
616    uint32_t remainingCapacity = getRemainingCacheCapacity();
617    uint32_t precacheIdx = 0;
618    while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
619        mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
620        remainingCapacity = getRemainingCacheCapacity();
621        precacheIdx ++;
622    }
623}
624
625void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
626    uint32_t currentNumFonts = mActiveFonts.size();
627    mCurrentFont = Font::create(this, fontId, fontSize);
628
629    const float maxPrecacheFontSize = 40.0f;
630    bool isNewFont = currentNumFonts != mActiveFonts.size();
631
632    if (isNewFont && fontSize <= maxPrecacheFontSize) {
633        precacheLatin(paint);
634    }
635}
636
637FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
638        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
639    checkInit();
640
641    if (!mCurrentFont) {
642        DropShadow image;
643        image.width = 0;
644        image.height = 0;
645        image.image = NULL;
646        image.penX = 0;
647        image.penY = 0;
648        return image;
649    }
650
651    Rect bounds;
652    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
653    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
654    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
655    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
656    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
657        dataBuffer[i] = 0;
658    }
659
660    int penX = radius - bounds.left;
661    int penY = radius - bounds.bottom;
662
663    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
664            dataBuffer, paddedWidth, paddedHeight);
665    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
666
667    DropShadow image;
668    image.width = paddedWidth;
669    image.height = paddedHeight;
670    image.image = dataBuffer;
671    image.penX = penX;
672    image.penY = penY;
673    return image;
674}
675
676void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
677        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
678    checkInit();
679
680    if (!mCurrentFont) {
681        LOGE("No font set");
682        return;
683    }
684
685    mClip = clip;
686    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
687
688    if (mCurrentQuadIndex != 0) {
689        issueDrawCommand();
690        mCurrentQuadIndex = 0;
691    }
692}
693
694void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
695    // Compute gaussian weights for the blur
696    // e is the euler's number
697    float e = 2.718281828459045f;
698    float pi = 3.1415926535897932f;
699    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
700    // x is of the form [-radius .. 0 .. radius]
701    // and sigma varies with radius.
702    // Based on some experimental radius values and sigma's
703    // we approximately fit sigma = f(radius) as
704    // sigma = radius * 0.3  + 0.6
705    // The larger the radius gets, the more our gaussian blur
706    // will resemble a box blur since with large sigma
707    // the gaussian curve begins to lose its shape
708    float sigma = 0.3f * (float)radius + 0.6f;
709
710    // Now compute the coefficints
711    // We will store some redundant values to save some math during
712    // the blur calculations
713    // precompute some values
714    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
715    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
716
717    float normalizeFactor = 0.0f;
718    for(int32_t r = -radius; r <= radius; r ++) {
719        float floatR = (float) r;
720        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
721        normalizeFactor += weights[r + radius];
722    }
723
724    //Now we need to normalize the weights because all our coefficients need to add up to one
725    normalizeFactor = 1.0f / normalizeFactor;
726    for(int32_t r = -radius; r <= radius; r ++) {
727        weights[r + radius] *= normalizeFactor;
728    }
729}
730
731void FontRenderer::horizontalBlur(float* weights, int32_t radius,
732        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
733    float blurredPixel = 0.0f;
734    float currentPixel = 0.0f;
735
736    for(int32_t y = 0; y < height; y ++) {
737
738        const uint8_t* input = source + y * width;
739        uint8_t* output = dest + y * width;
740
741        for(int32_t x = 0; x < width; x ++) {
742            blurredPixel = 0.0f;
743            const float* gPtr = weights;
744            // Optimization for non-border pixels
745            if ((x > radius) && (x < (width - radius))) {
746                const uint8_t *i = input + (x - radius);
747                for(int r = -radius; r <= radius; r ++) {
748                    currentPixel = (float) (*i);
749                    blurredPixel += currentPixel * gPtr[0];
750                    gPtr++;
751                    i++;
752                }
753            } else {
754                for(int32_t r = -radius; r <= radius; r ++) {
755                    // Stepping left and right away from the pixel
756                    int validW = x + r;
757                    if(validW < 0) {
758                        validW = 0;
759                    }
760                    if(validW > width - 1) {
761                        validW = width - 1;
762                    }
763
764                    currentPixel = (float)(input[validW]);
765                    blurredPixel += currentPixel * gPtr[0];
766                    gPtr++;
767                }
768            }
769            *output = (uint8_t)blurredPixel;
770            output ++;
771        }
772    }
773}
774
775void FontRenderer::verticalBlur(float* weights, int32_t radius,
776        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
777    float blurredPixel = 0.0f;
778    float currentPixel = 0.0f;
779
780    for(int32_t y = 0; y < height; y ++) {
781
782        uint8_t* output = dest + y * width;
783
784        for(int32_t x = 0; x < width; x ++) {
785            blurredPixel = 0.0f;
786            const float* gPtr = weights;
787            const uint8_t* input = source + x;
788            // Optimization for non-border pixels
789            if ((y > radius) && (y < (height - radius))) {
790                const uint8_t *i = input + ((y - radius) * width);
791                for(int32_t r = -radius; r <= radius; r ++) {
792                    currentPixel = (float)(*i);
793                    blurredPixel += currentPixel * gPtr[0];
794                    gPtr++;
795                    i += width;
796                }
797            } else {
798                for(int32_t r = -radius; r <= radius; r ++) {
799                    int validH = y + r;
800                    // Clamp to zero and width
801                    if(validH < 0) {
802                        validH = 0;
803                    }
804                    if(validH > height - 1) {
805                        validH = height - 1;
806                    }
807
808                    const uint8_t *i = input + validH * width;
809                    currentPixel = (float)(*i);
810                    blurredPixel += currentPixel * gPtr[0];
811                    gPtr++;
812                }
813            }
814            *output = (uint8_t)blurredPixel;
815            output ++;
816        }
817    }
818}
819
820
821void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
822    float *gaussian = new float[2 * radius + 1];
823    computeGaussianWeights(gaussian, radius);
824    uint8_t* scratch = new uint8_t[width * height];
825    horizontalBlur(gaussian, radius, image, scratch, width, height);
826    verticalBlur(gaussian, radius, scratch, image, width, height);
827    delete[] gaussian;
828    delete[] scratch;
829}
830
831}; // namespace uirenderer
832}; // namespace android
833