FontRenderer.cpp revision 7975fb6d12cb1eb96b75e3a563627cd4c4081bd6
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_LINEAR);
439    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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, 18, nextLine, 0));
447    nextLine += mCacheLines.top()->mMaxHeight;
448    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, 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, 34, 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, 42, 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}
634
635FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
636        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
637    checkInit();
638
639    if (!mCurrentFont) {
640        DropShadow image;
641        image.width = 0;
642        image.height = 0;
643        image.image = NULL;
644        image.penX = 0;
645        image.penY = 0;
646        return image;
647    }
648
649    Rect bounds;
650    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
651    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
652    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
653    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
654    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
655        dataBuffer[i] = 0;
656    }
657
658    int penX = radius - bounds.left;
659    int penY = radius - bounds.bottom;
660
661    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
662            dataBuffer, paddedWidth, paddedHeight);
663    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
664
665    DropShadow image;
666    image.width = paddedWidth;
667    image.height = paddedHeight;
668    image.image = dataBuffer;
669    image.penX = penX;
670    image.penY = penY;
671    return image;
672}
673
674void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
675        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
676    checkInit();
677
678    if (!mCurrentFont) {
679        LOGE("No font set");
680        return;
681    }
682
683    mClip = clip;
684    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
685
686    if (mCurrentQuadIndex != 0) {
687        issueDrawCommand();
688        mCurrentQuadIndex = 0;
689    }
690}
691
692void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
693    // Compute gaussian weights for the blur
694    // e is the euler's number
695    float e = 2.718281828459045f;
696    float pi = 3.1415926535897932f;
697    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
698    // x is of the form [-radius .. 0 .. radius]
699    // and sigma varies with radius.
700    // Based on some experimental radius values and sigma's
701    // we approximately fit sigma = f(radius) as
702    // sigma = radius * 0.3  + 0.6
703    // The larger the radius gets, the more our gaussian blur
704    // will resemble a box blur since with large sigma
705    // the gaussian curve begins to lose its shape
706    float sigma = 0.3f * (float)radius + 0.6f;
707
708    // Now compute the coefficints
709    // We will store some redundant values to save some math during
710    // the blur calculations
711    // precompute some values
712    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
713    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
714
715    float normalizeFactor = 0.0f;
716    for(int32_t r = -radius; r <= radius; r ++) {
717        float floatR = (float) r;
718        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
719        normalizeFactor += weights[r + radius];
720    }
721
722    //Now we need to normalize the weights because all our coefficients need to add up to one
723    normalizeFactor = 1.0f / normalizeFactor;
724    for(int32_t r = -radius; r <= radius; r ++) {
725        weights[r + radius] *= normalizeFactor;
726    }
727}
728
729void FontRenderer::horizontalBlur(float* weights, int32_t radius,
730        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
731    float blurredPixel = 0.0f;
732    float currentPixel = 0.0f;
733
734    for(int32_t y = 0; y < height; y ++) {
735
736        const uint8_t* input = source + y * width;
737        uint8_t* output = dest + y * width;
738
739        for(int32_t x = 0; x < width; x ++) {
740            blurredPixel = 0.0f;
741            const float* gPtr = weights;
742            // Optimization for non-border pixels
743            if ((x > radius) && (x < (width - radius))) {
744                const uint8_t *i = input + (x - radius);
745                for(int r = -radius; r <= radius; r ++) {
746                    currentPixel = (float) (*i);
747                    blurredPixel += currentPixel * gPtr[0];
748                    gPtr++;
749                    i++;
750                }
751            } else {
752                for(int32_t r = -radius; r <= radius; r ++) {
753                    // Stepping left and right away from the pixel
754                    int validW = x + r;
755                    if(validW < 0) {
756                        validW = 0;
757                    }
758                    if(validW > width - 1) {
759                        validW = width - 1;
760                    }
761
762                    currentPixel = (float)(input[validW]);
763                    blurredPixel += currentPixel * gPtr[0];
764                    gPtr++;
765                }
766            }
767            *output = (uint8_t)blurredPixel;
768            output ++;
769        }
770    }
771}
772
773void FontRenderer::verticalBlur(float* weights, int32_t radius,
774        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
775    float blurredPixel = 0.0f;
776    float currentPixel = 0.0f;
777
778    for(int32_t y = 0; y < height; y ++) {
779
780        uint8_t* output = dest + y * width;
781
782        for(int32_t x = 0; x < width; x ++) {
783            blurredPixel = 0.0f;
784            const float* gPtr = weights;
785            const uint8_t* input = source + x;
786            // Optimization for non-border pixels
787            if ((y > radius) && (y < (height - radius))) {
788                const uint8_t *i = input + ((y - radius) * width);
789                for(int32_t r = -radius; r <= radius; r ++) {
790                    currentPixel = (float)(*i);
791                    blurredPixel += currentPixel * gPtr[0];
792                    gPtr++;
793                    i += width;
794                }
795            } else {
796                for(int32_t r = -radius; r <= radius; r ++) {
797                    int validH = y + r;
798                    // Clamp to zero and width
799                    if(validH < 0) {
800                        validH = 0;
801                    }
802                    if(validH > height - 1) {
803                        validH = height - 1;
804                    }
805
806                    const uint8_t *i = input + validH * width;
807                    currentPixel = (float)(*i);
808                    blurredPixel += currentPixel * gPtr[0];
809                    gPtr++;
810                }
811            }
812            *output = (uint8_t)blurredPixel;
813            output ++;
814        }
815    }
816}
817
818
819void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
820    float *gaussian = new float[2 * radius + 1];
821    computeGaussianWeights(gaussian, radius);
822    uint8_t* scratch = new uint8_t[width * height];
823    horizontalBlur(gaussian, radius, image, scratch, width, height);
824    verticalBlur(gaussian, radius, scratch, image, width, height);
825    delete[] gaussian;
826    delete[] scratch;
827}
828
829}; // namespace uirenderer
830}; // namespace android
831