FontRenderer.cpp revision f18136cb3c881a9d16c1a4f0f341732c276936bf
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,
147                       uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
148    if(bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
149        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP,
150                   bitmap, bitmapW, bitmapH, NULL);
151    }
152    else {
153        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER,
154                   NULL, 0, 0, NULL);
155    }
156
157}
158
159void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
160                       int numGlyphs, Rect *bounds) {
161    if(bounds == NULL) {
162        LOGE("No return rectangle provided to measure text");
163        return;
164    }
165    bounds->set(1e6, -1e6, -1e6, 1e6);
166    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
167}
168
169void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
170                       int numGlyphs, int x, int y, RenderMode mode,
171                       uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
172                       Rect *bounds) {
173    if (numGlyphs == 0 || text == NULL || len == 0) {
174        return;
175    }
176
177    int penX = x, penY = y;
178    int glyphsLeft = 1;
179    if (numGlyphs > 0) {
180        glyphsLeft = numGlyphs;
181    }
182
183    text += start;
184
185    while (glyphsLeft > 0) {
186        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
187
188        // Reached the end of the string or encountered
189        if (utfChar < 0) {
190            break;
191        }
192
193        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
194
195        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
196        if (cachedGlyph->mIsValid) {
197            switch(mode) {
198            case FRAMEBUFFER:
199                drawCachedGlyph(cachedGlyph, penX, penY);
200                break;
201            case BITMAP:
202                drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
203                break;
204            case MEASURE:
205                measureCachedGlyph(cachedGlyph, penX, penY, bounds);
206                break;
207            }
208        }
209
210        penX += SkFixedFloor(cachedGlyph->mAdvanceX);
211
212        // If we were given a specific number of glyphs, decrement
213        if (numGlyphs > 0) {
214            glyphsLeft--;
215        }
216    }
217}
218
219void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
220    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
221    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
222    glyph->mBitmapLeft = skiaGlyph.fLeft;
223    glyph->mBitmapTop = skiaGlyph.fTop;
224
225    uint32_t startX = 0;
226    uint32_t startY = 0;
227
228    // Get the bitmap for the glyph
229    paint->findImage(skiaGlyph);
230    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
231
232    if (!glyph->mIsValid) {
233        return;
234    }
235
236    uint32_t endX = startX + skiaGlyph.fWidth;
237    uint32_t endY = startY + skiaGlyph.fHeight;
238
239    glyph->mStartX = startX;
240    glyph->mStartY = startY;
241    glyph->mBitmapWidth = skiaGlyph.fWidth;
242    glyph->mBitmapHeight = skiaGlyph.fHeight;
243
244    uint32_t cacheWidth = mState->getCacheWidth();
245    uint32_t cacheHeight = mState->getCacheHeight();
246
247    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
248    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
249    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
250    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
251
252    mState->mUploadTexture = true;
253}
254
255Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
256    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
257    mCachedGlyphs.add(glyph, newGlyph);
258
259    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
260    newGlyph->mGlyphIndex = skiaGlyph.fID;
261    newGlyph->mIsValid = false;
262
263    updateGlyphCache(paint, skiaGlyph, newGlyph);
264
265    return newGlyph;
266}
267
268Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
269    Vector<Font*> &activeFonts = state->mActiveFonts;
270
271    for (uint32_t i = 0; i < activeFonts.size(); i++) {
272        Font* font = activeFonts[i];
273        if (font->mFontId == fontId && font->mFontSize == fontSize) {
274            return font;
275        }
276    }
277
278    Font* newFont = new Font(state, fontId, fontSize);
279    activeFonts.push(newFont);
280    return newFont;
281}
282
283///////////////////////////////////////////////////////////////////////////////
284// FontRenderer
285///////////////////////////////////////////////////////////////////////////////
286
287FontRenderer::FontRenderer() {
288    LOGD("Creating FontRenderer");
289
290    mInitialized = false;
291    mMaxNumberOfQuads = 1024;
292    mCurrentQuadIndex = 0;
293    mTextureId = 0;
294
295    mIndexBufferID = 0;
296
297    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
298    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
299
300    char property[PROPERTY_VALUE_MAX];
301    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
302        LOGD("  Setting text cache width to %s pixels", property);
303        mCacheWidth = atoi(property);
304    } else {
305        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
306    }
307
308    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
309        LOGD("  Setting text cache width to %s pixels", property);
310        mCacheHeight = atoi(property);
311    } else {
312        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
313    }
314}
315
316FontRenderer::~FontRenderer() {
317    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
318        delete mCacheLines[i];
319    }
320    mCacheLines.clear();
321
322    delete[] mTextMeshPtr;
323    delete[] mTextTexture;
324
325    if(mTextureId) {
326        glDeleteTextures(1, &mTextureId);
327    }
328
329    Vector<Font*> fontsToDereference = mActiveFonts;
330    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
331        delete fontsToDereference[i];
332    }
333}
334
335void FontRenderer::flushAllAndInvalidate() {
336    if (mCurrentQuadIndex != 0) {
337        issueDrawCommand();
338        mCurrentQuadIndex = 0;
339    }
340    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
341        mActiveFonts[i]->invalidateTextureCache();
342    }
343    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
344        mCacheLines[i]->mCurrentCol = 0;
345    }
346}
347
348bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
349    // If the glyph is too tall, don't cache it
350    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
351        LOGE("Font size to large to fit in cache. width, height = %i, %i",
352                (int) glyph.fWidth, (int) glyph.fHeight);
353        return false;
354    }
355
356    // Now copy the bitmap into the cache texture
357    uint32_t startX = 0;
358    uint32_t startY = 0;
359
360    bool bitmapFit = false;
361    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
362        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
363        if (bitmapFit) {
364            break;
365        }
366    }
367
368    // If the new glyph didn't fit, flush the state so far and invalidate everything
369    if (!bitmapFit) {
370        flushAllAndInvalidate();
371
372        // Try to fit it again
373        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
374            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
375            if (bitmapFit) {
376                break;
377            }
378        }
379
380        // if we still don't fit, something is wrong and we shouldn't draw
381        if (!bitmapFit) {
382            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
383                    (int) glyph.fWidth, (int) glyph.fHeight);
384            return false;
385        }
386    }
387
388    *retOriginX = startX;
389    *retOriginY = startY;
390
391    uint32_t endX = startX + glyph.fWidth;
392    uint32_t endY = startY + glyph.fHeight;
393
394    uint32_t cacheWidth = mCacheWidth;
395
396    uint8_t* cacheBuffer = mTextTexture;
397    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
398    unsigned int stride = glyph.rowBytes();
399
400    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
401    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
402        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
403            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
404            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
405        }
406    }
407
408    return true;
409}
410
411void FontRenderer::initTextTexture() {
412    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
413    mUploadTexture = false;
414
415    glGenTextures(1, &mTextureId);
416    glBindTexture(GL_TEXTURE_2D, mTextureId);
417    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
418    // Initialize texture dimentions
419    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
420                  GL_ALPHA, GL_UNSIGNED_BYTE, 0);
421
422    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
423    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
424
425    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
426    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
427
428    // Split up our cache texture into lines of certain widths
429    int nextLine = 0;
430    mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
431    nextLine += mCacheLines.top()->mMaxHeight;
432    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
433    nextLine += mCacheLines.top()->mMaxHeight;
434    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
435    nextLine += mCacheLines.top()->mMaxHeight;
436    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
437    nextLine += mCacheLines.top()->mMaxHeight;
438    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
439    nextLine += mCacheLines.top()->mMaxHeight;
440    mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
441    nextLine += mCacheLines.top()->mMaxHeight;
442    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
443}
444
445// Avoid having to reallocate memory and render quad by quad
446void FontRenderer::initVertexArrayBuffers() {
447    uint32_t numIndicies = mMaxNumberOfQuads * 6;
448    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
449    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
450
451    // Four verts, two triangles , six indices per quad
452    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
453        int i6 = i * 6;
454        int i4 = i * 4;
455
456        indexBufferData[i6 + 0] = i4 + 0;
457        indexBufferData[i6 + 1] = i4 + 1;
458        indexBufferData[i6 + 2] = i4 + 2;
459
460        indexBufferData[i6 + 3] = i4 + 0;
461        indexBufferData[i6 + 4] = i4 + 2;
462        indexBufferData[i6 + 5] = i4 + 3;
463    }
464
465    glGenBuffers(1, &mIndexBufferID);
466    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
467    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
468    glBindBuffer(GL_ARRAY_BUFFER, 0);
469
470    free(indexBufferData);
471
472    uint32_t coordSize = 3;
473    uint32_t uvSize = 2;
474    uint32_t vertsPerQuad = 4;
475    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
476    mTextMeshPtr = new float[vertexBufferSize];
477}
478
479// We don't want to allocate anything unless we actually draw text
480void FontRenderer::checkInit() {
481    if (mInitialized) {
482        return;
483    }
484
485    initTextTexture();
486    initVertexArrayBuffers();
487
488    // We store a string with letters in a rough frequency of occurrence
489    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
490    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
491    mLatinPrecache += String16(",.?!()-+@;:`'");
492    mLatinPrecache += String16("0123456789");
493
494    mInitialized = true;
495}
496
497void FontRenderer::checkTextureUpdate() {
498    if (!mUploadTexture) {
499        return;
500    }
501
502    glBindTexture(GL_TEXTURE_2D, mTextureId);
503
504    // Iterate over all the cache lines and see which ones need to be updated
505    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
506        CacheTextureLine* cl = mCacheLines[i];
507        if(cl->mDirty) {
508            uint32_t xOffset = 0;
509            uint32_t yOffset = cl->mCurrentRow;
510            uint32_t width   = mCacheWidth;
511            uint32_t height  = cl->mMaxHeight;
512            void*    textureData = mTextTexture + yOffset*width;
513
514            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
515                             GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
516
517            cl->mDirty = false;
518        }
519    }
520
521    mUploadTexture = false;
522}
523
524void FontRenderer::issueDrawCommand() {
525
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