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