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