FontRenderer.cpp revision 325a0f969c1d803d7e39a9caee8cc3d400350659
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, int flags) :
42    mState(state), mFontId(fontId), mFontSize(fontSize), mFlags(flags) {
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, int flags) {
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 && font->mFlags == flags) {
284            return font;
285        }
286    }
287
288    Font* newFont = new Font(state, fontId, fontSize, flags);
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    mDrawn = true;
561}
562
563void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
564        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
565        float x4, float y4, float z4, float u4, float v4) {
566    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
567        return;
568    }
569
570    const uint32_t vertsPerQuad = 4;
571    const uint32_t floatsPerVert = 5;
572    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
573
574    (*currentPos++) = x1;
575    (*currentPos++) = y1;
576    (*currentPos++) = z1;
577    (*currentPos++) = u1;
578    (*currentPos++) = v1;
579
580    (*currentPos++) = x2;
581    (*currentPos++) = y2;
582    (*currentPos++) = z2;
583    (*currentPos++) = u2;
584    (*currentPos++) = v2;
585
586    (*currentPos++) = x3;
587    (*currentPos++) = y3;
588    (*currentPos++) = z3;
589    (*currentPos++) = u3;
590    (*currentPos++) = v3;
591
592    (*currentPos++) = x4;
593    (*currentPos++) = y4;
594    (*currentPos++) = z4;
595    (*currentPos++) = u4;
596    (*currentPos++) = v4;
597
598    mCurrentQuadIndex++;
599
600    if (mBounds) {
601        mBounds->left = fmin(mBounds->left, x1);
602        mBounds->top = fmin(mBounds->top, y3);
603        mBounds->right = fmax(mBounds->right, x3);
604        mBounds->bottom = fmax(mBounds->bottom, y1);
605    }
606
607    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
608        issueDrawCommand();
609        mCurrentQuadIndex = 0;
610    }
611}
612
613uint32_t FontRenderer::getRemainingCacheCapacity() {
614    uint32_t remainingCapacity = 0;
615    float totalPixels = 0;
616    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
617         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
618         totalPixels += mCacheLines[i]->mMaxWidth;
619    }
620    remainingCapacity = (remainingCapacity * 100) / totalPixels;
621    return remainingCapacity;
622}
623
624void FontRenderer::precacheLatin(SkPaint* paint) {
625    // Remaining capacity is measured in %
626    uint32_t remainingCapacity = getRemainingCacheCapacity();
627    uint32_t precacheIdx = 0;
628    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
629        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
630        remainingCapacity = getRemainingCacheCapacity();
631        precacheIdx ++;
632    }
633}
634
635void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
636    uint32_t currentNumFonts = mActiveFonts.size();
637    int flags = 0;
638    if (paint->isFakeBoldText()) {
639        flags |= Font::kFakeBold;
640    }
641    mCurrentFont = Font::create(this, fontId, fontSize, flags);
642
643    const float maxPrecacheFontSize = 40.0f;
644    bool isNewFont = currentNumFonts != mActiveFonts.size();
645
646    if (isNewFont && fontSize <= maxPrecacheFontSize) {
647        precacheLatin(paint);
648    }
649}
650
651FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
652        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
653    checkInit();
654
655    if (!mCurrentFont) {
656        DropShadow image;
657        image.width = 0;
658        image.height = 0;
659        image.image = NULL;
660        image.penX = 0;
661        image.penY = 0;
662        return image;
663    }
664
665    Rect bounds;
666    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
667    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
668    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
669    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
670    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
671        dataBuffer[i] = 0;
672    }
673
674    int penX = radius - bounds.left;
675    int penY = radius - bounds.bottom;
676
677    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
678            dataBuffer, paddedWidth, paddedHeight);
679    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
680
681    DropShadow image;
682    image.width = paddedWidth;
683    image.height = paddedHeight;
684    image.image = dataBuffer;
685    image.penX = penX;
686    image.penY = penY;
687    return image;
688}
689
690bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
691        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
692    checkInit();
693
694    if (!mCurrentFont) {
695        LOGE("No font set");
696        return false;
697    }
698
699    mDrawn = false;
700    mBounds = bounds;
701    mClip = clip;
702    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
703    mBounds = NULL;
704
705    if (mCurrentQuadIndex != 0) {
706        issueDrawCommand();
707        mCurrentQuadIndex = 0;
708    }
709
710    return mDrawn;
711}
712
713void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
714    // Compute gaussian weights for the blur
715    // e is the euler's number
716    float e = 2.718281828459045f;
717    float pi = 3.1415926535897932f;
718    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
719    // x is of the form [-radius .. 0 .. radius]
720    // and sigma varies with radius.
721    // Based on some experimental radius values and sigma's
722    // we approximately fit sigma = f(radius) as
723    // sigma = radius * 0.3  + 0.6
724    // The larger the radius gets, the more our gaussian blur
725    // will resemble a box blur since with large sigma
726    // the gaussian curve begins to lose its shape
727    float sigma = 0.3f * (float) radius + 0.6f;
728
729    // Now compute the coefficints
730    // We will store some redundant values to save some math during
731    // the blur calculations
732    // precompute some values
733    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
734    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
735
736    float normalizeFactor = 0.0f;
737    for (int32_t r = -radius; r <= radius; r ++) {
738        float floatR = (float) r;
739        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
740        normalizeFactor += weights[r + radius];
741    }
742
743    //Now we need to normalize the weights because all our coefficients need to add up to one
744    normalizeFactor = 1.0f / normalizeFactor;
745    for (int32_t r = -radius; r <= radius; r ++) {
746        weights[r + radius] *= normalizeFactor;
747    }
748}
749
750void FontRenderer::horizontalBlur(float* weights, int32_t radius,
751        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
752    float blurredPixel = 0.0f;
753    float currentPixel = 0.0f;
754
755    for (int32_t y = 0; y < height; y ++) {
756
757        const uint8_t* input = source + y * width;
758        uint8_t* output = dest + y * width;
759
760        for (int32_t x = 0; x < width; x ++) {
761            blurredPixel = 0.0f;
762            const float* gPtr = weights;
763            // Optimization for non-border pixels
764            if (x > radius && x < (width - radius)) {
765                const uint8_t *i = input + (x - radius);
766                for (int r = -radius; r <= radius; r ++) {
767                    currentPixel = (float) (*i);
768                    blurredPixel += currentPixel * gPtr[0];
769                    gPtr++;
770                    i++;
771                }
772            } else {
773                for (int32_t r = -radius; r <= radius; r ++) {
774                    // Stepping left and right away from the pixel
775                    int validW = x + r;
776                    if (validW < 0) {
777                        validW = 0;
778                    }
779                    if (validW > width - 1) {
780                        validW = width - 1;
781                    }
782
783                    currentPixel = (float) input[validW];
784                    blurredPixel += currentPixel * gPtr[0];
785                    gPtr++;
786                }
787            }
788            *output = (uint8_t)blurredPixel;
789            output ++;
790        }
791    }
792}
793
794void FontRenderer::verticalBlur(float* weights, int32_t radius,
795        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
796    float blurredPixel = 0.0f;
797    float currentPixel = 0.0f;
798
799    for (int32_t y = 0; y < height; y ++) {
800
801        uint8_t* output = dest + y * width;
802
803        for (int32_t x = 0; x < width; x ++) {
804            blurredPixel = 0.0f;
805            const float* gPtr = weights;
806            const uint8_t* input = source + x;
807            // Optimization for non-border pixels
808            if (y > radius && y < (height - radius)) {
809                const uint8_t *i = input + ((y - radius) * width);
810                for (int32_t r = -radius; r <= radius; r ++) {
811                    currentPixel = (float)(*i);
812                    blurredPixel += currentPixel * gPtr[0];
813                    gPtr++;
814                    i += width;
815                }
816            } else {
817                for (int32_t r = -radius; r <= radius; r ++) {
818                    int validH = y + r;
819                    // Clamp to zero and width
820                    if (validH < 0) {
821                        validH = 0;
822                    }
823                    if (validH > height - 1) {
824                        validH = height - 1;
825                    }
826
827                    const uint8_t *i = input + validH * width;
828                    currentPixel = (float) (*i);
829                    blurredPixel += currentPixel * gPtr[0];
830                    gPtr++;
831                }
832            }
833            *output = (uint8_t) blurredPixel;
834            output ++;
835        }
836    }
837}
838
839
840void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
841    float *gaussian = new float[2 * radius + 1];
842    computeGaussianWeights(gaussian, radius);
843    uint8_t* scratch = new uint8_t[width * height];
844    horizontalBlur(gaussian, radius, image, scratch, width, height);
845    verticalBlur(gaussian, radius, scratch, image, width, height);
846    delete[] gaussian;
847    delete[] scratch;
848}
849
850}; // namespace uirenderer
851}; // namespace android
852