FontRenderer.cpp revision 5a3ec7113c99242fa0d0a328a0ec3892698203f1
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 "Caches.h"
26#include "Debug.h"
27#include "FontRenderer.h"
28#include "Caches.h"
29
30namespace android {
31namespace uirenderer {
32
33///////////////////////////////////////////////////////////////////////////////
34// Defines
35///////////////////////////////////////////////////////////////////////////////
36
37#define DEFAULT_TEXT_CACHE_WIDTH 1024
38#define DEFAULT_TEXT_CACHE_HEIGHT 256
39#define MAX_TEXT_CACHE_WIDTH 2048
40#define CACHE_BLOCK_ROUNDING_SIZE 4
41
42#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
43
44///////////////////////////////////////////////////////////////////////////////
45// CacheBlock
46///////////////////////////////////////////////////////////////////////////////
47
48/**
49 * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
50 * order, except for the final block (the remainder space at the right, since we fill from the
51 * left).
52 */
53CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
54#if DEBUG_FONT_RENDERER
55    ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
56            newBlock, newBlock->mX, newBlock->mY,
57            newBlock->mWidth, newBlock->mHeight);
58#endif
59    CacheBlock *currBlock = head;
60    CacheBlock *prevBlock = NULL;
61    while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
62        if (newBlock->mWidth < currBlock->mWidth) {
63            newBlock->mNext = currBlock;
64            newBlock->mPrev = prevBlock;
65            currBlock->mPrev = newBlock;
66            if (prevBlock) {
67                prevBlock->mNext = newBlock;
68                return head;
69            } else {
70                return newBlock;
71            }
72        }
73        prevBlock = currBlock;
74        currBlock = currBlock->mNext;
75    }
76    // new block larger than all others - insert at end (but before the remainder space, if there)
77    newBlock->mNext = currBlock;
78    newBlock->mPrev = prevBlock;
79    if (currBlock) {
80        currBlock->mPrev = newBlock;
81    }
82    if (prevBlock) {
83        prevBlock->mNext = newBlock;
84        return head;
85    } else {
86        return newBlock;
87    }
88}
89
90CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
91#if DEBUG_FONT_RENDERER
92    ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
93            blockToRemove, blockToRemove->mX, blockToRemove->mY,
94            blockToRemove->mWidth, blockToRemove->mHeight);
95#endif
96    CacheBlock* newHead = head;
97    CacheBlock* nextBlock = blockToRemove->mNext;
98    CacheBlock* prevBlock = blockToRemove->mPrev;
99    if (prevBlock) {
100        prevBlock->mNext = nextBlock;
101    } else {
102        newHead = nextBlock;
103    }
104    if (nextBlock) {
105        nextBlock->mPrev = prevBlock;
106    }
107    delete blockToRemove;
108    return newHead;
109}
110
111///////////////////////////////////////////////////////////////////////////////
112// CacheTexture
113///////////////////////////////////////////////////////////////////////////////
114
115bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
116    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
117        return false;
118    }
119
120    uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
121    uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
122    // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
123    // This columns for glyphs that are close but not necessarily exactly the same size. It trades
124    // off the loss of a few pixels for some glyphs against the ability to store more glyphs
125    // of varying sizes in one block.
126    uint16_t roundedUpW =
127            (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
128    CacheBlock *cacheBlock = mCacheBlocks;
129    while (cacheBlock) {
130        // Store glyph in this block iff: it fits the block's remaining space and:
131        // it's the remainder space (mY == 0) or there's only enough height for this one glyph
132        // or it's within ROUNDING_SIZE of the block width
133        if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
134                (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
135                        (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
136            if (cacheBlock->mHeight - glyphH < glyphH) {
137                // Only enough space for this glyph - don't bother rounding up the width
138                roundedUpW = glyphW;
139            }
140            *retOriginX = cacheBlock->mX;
141            *retOriginY = cacheBlock->mY;
142            // If this is the remainder space, create a new cache block for this column. Otherwise,
143            // adjust the info about this column.
144            if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
145                uint16_t oldX = cacheBlock->mX;
146                // Adjust remainder space dimensions
147                cacheBlock->mWidth -= roundedUpW;
148                cacheBlock->mX += roundedUpW;
149                if (mHeight - glyphH >= glyphH) {
150                    // There's enough height left over to create a new CacheBlock
151                    CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
152                            roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
153#if DEBUG_FONT_RENDERER
154                    ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
155                            newBlock, newBlock->mX, newBlock->mY,
156                            newBlock->mWidth, newBlock->mHeight);
157#endif
158                    mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
159                }
160            } else {
161                // Insert into current column and adjust column dimensions
162                cacheBlock->mY += glyphH;
163                cacheBlock->mHeight -= glyphH;
164#if DEBUG_FONT_RENDERER
165                ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
166                        cacheBlock, cacheBlock->mX, cacheBlock->mY,
167                        cacheBlock->mWidth, cacheBlock->mHeight);
168#endif
169            }
170            if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
171                // If remaining space in this block is too small to be useful, remove it
172                mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
173            }
174            mDirty = true;
175#if DEBUG_FONT_RENDERER
176            ALOGD("fitBitmap: current block list:");
177            mCacheBlocks->output();
178#endif
179            ++mNumGlyphs;
180            return true;
181        }
182        cacheBlock = cacheBlock->mNext;
183    }
184#if DEBUG_FONT_RENDERER
185    ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
186#endif
187    return false;
188}
189
190///////////////////////////////////////////////////////////////////////////////
191// Font
192///////////////////////////////////////////////////////////////////////////////
193
194Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
195        int flags, uint32_t italicStyle, uint32_t scaleX,
196        SkPaint::Style style, uint32_t strokeWidth) :
197        mState(state), mFontId(fontId), mFontSize(fontSize),
198        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
199        mStyle(style), mStrokeWidth(mStrokeWidth) {
200}
201
202
203Font::~Font() {
204    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
205        if (mState->mActiveFonts[ct] == this) {
206            mState->mActiveFonts.removeAt(ct);
207            break;
208        }
209    }
210
211    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
212        delete mCachedGlyphs.valueAt(i);
213    }
214}
215
216void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
217    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
218        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
219        if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
220            cachedGlyph->mIsValid = false;
221        }
222    }
223}
224
225void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
226        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
227    int nPenX = x + glyph->mBitmapLeft;
228    int nPenY = y + glyph->mBitmapTop;
229
230    int width = (int) glyph->mBitmapWidth;
231    int height = (int) glyph->mBitmapHeight;
232
233    if (bounds->bottom > nPenY) {
234        bounds->bottom = nPenY;
235    }
236    if (bounds->left > nPenX) {
237        bounds->left = nPenX;
238    }
239    if (bounds->right < nPenX + width) {
240        bounds->right = nPenX + width;
241    }
242    if (bounds->top < nPenY + height) {
243        bounds->top = nPenY + height;
244    }
245}
246
247void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
248        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
249    int nPenX = x + glyph->mBitmapLeft;
250    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
251
252    float u1 = glyph->mBitmapMinU;
253    float u2 = glyph->mBitmapMaxU;
254    float v1 = glyph->mBitmapMinV;
255    float v2 = glyph->mBitmapMaxV;
256
257    int width = (int) glyph->mBitmapWidth;
258    int height = (int) glyph->mBitmapHeight;
259
260    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
261            nPenX + width, nPenY, u2, v2,
262            nPenX + width, nPenY - height, u2, v1,
263            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
264}
265
266void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
267        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
268    int nPenX = x + glyph->mBitmapLeft;
269    int nPenY = y + glyph->mBitmapTop;
270
271    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
272    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
273
274    CacheTexture *cacheTexture = glyph->mCacheTexture;
275    uint32_t cacheWidth = cacheTexture->mWidth;
276    const uint8_t* cacheBuffer = cacheTexture->mTexture;
277
278    uint32_t cacheX = 0, cacheY = 0;
279    int32_t bX = 0, bY = 0;
280    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
281        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
282#if DEBUG_FONT_RENDERER
283            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
284                ALOGE("Skipping invalid index");
285                continue;
286            }
287#endif
288            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
289            bitmap[bY * bitmapW + bX] = tempCol;
290        }
291    }
292}
293
294void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
295        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
296    const float halfWidth = glyph->mBitmapWidth * 0.5f;
297    const float height = glyph->mBitmapHeight;
298
299    vOffset += glyph->mBitmapTop + height;
300
301    SkPoint destination[4];
302    measure.getPosTan(x + hOffset +  glyph->mBitmapLeft + halfWidth, position, tangent);
303
304    // Move along the tangent and offset by the normal
305    destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
306            -tangent->fY * halfWidth + tangent->fX * vOffset);
307    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
308            tangent->fY * halfWidth + tangent->fX * vOffset);
309    destination[2].set(destination[1].fX + tangent->fY * height,
310            destination[1].fY - tangent->fX * height);
311    destination[3].set(destination[0].fX + tangent->fY * height,
312            destination[0].fY - tangent->fX * height);
313
314    const float u1 = glyph->mBitmapMinU;
315    const float u2 = glyph->mBitmapMaxU;
316    const float v1 = glyph->mBitmapMinV;
317    const float v2 = glyph->mBitmapMaxV;
318
319    mState->appendRotatedMeshQuad(
320            position->fX + destination[0].fX,
321            position->fY + destination[0].fY, u1, v2,
322            position->fX + destination[1].fX,
323            position->fY + destination[1].fY, u2, v2,
324            position->fX + destination[2].fX,
325            position->fY + destination[2].fY, u2, v1,
326            position->fX + destination[3].fX,
327            position->fY + destination[3].fY, u1, v1,
328            glyph->mCacheTexture);
329}
330
331CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
332    CachedGlyphInfo* cachedGlyph = NULL;
333    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
334    if (index >= 0) {
335        cachedGlyph = mCachedGlyphs.valueAt(index);
336    } else {
337        cachedGlyph = cacheGlyph(paint, textUnit);
338    }
339
340    // Is the glyph still in texture cache?
341    if (!cachedGlyph->mIsValid) {
342        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
343        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
344    }
345
346    return cachedGlyph;
347}
348
349void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
350        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
351    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
352        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
353                bitmapW, bitmapH, NULL, NULL);
354    } else {
355        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
356                0, 0, NULL, NULL);
357    }
358}
359
360void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
361            int numGlyphs, int x, int y, const float* positions) {
362    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
363            0, 0, NULL, positions);
364}
365
366void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
367        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
368    if (numGlyphs == 0 || text == NULL || len == 0) {
369        return;
370    }
371
372    text += start;
373
374    int glyphsCount = 0;
375    SkFixed prevRsbDelta = 0;
376
377    float penX = 0.0f;
378
379    SkPoint position;
380    SkVector tangent;
381
382    SkPathMeasure measure(*path, false);
383    float pathLength = SkScalarToFloat(measure.getLength());
384
385    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
386        float textWidth = SkScalarToFloat(paint->measureText(text, len));
387        float pathOffset = pathLength;
388        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
389            textWidth *= 0.5f;
390            pathOffset *= 0.5f;
391        }
392        penX += pathOffset - textWidth;
393    }
394
395    while (glyphsCount < numGlyphs && penX < pathLength) {
396        glyph_t glyph = GET_GLYPH(text);
397
398        if (IS_END_OF_STRING(glyph)) {
399            break;
400        }
401
402        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
403        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
404        prevRsbDelta = cachedGlyph->mRsbDelta;
405
406        if (cachedGlyph->mIsValid) {
407            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
408        }
409
410        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
411
412        glyphsCount++;
413    }
414}
415
416void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
417        int numGlyphs, Rect *bounds, const float* positions) {
418    if (bounds == NULL) {
419        ALOGE("No return rectangle provided to measure text");
420        return;
421    }
422    bounds->set(1e6, -1e6, -1e6, 1e6);
423    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
424}
425
426void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
427
428    if (numGlyphs == 0 || text == NULL) {
429        return;
430    }
431    int glyphsCount = 0;
432
433    while (glyphsCount < numGlyphs) {
434        glyph_t glyph = GET_GLYPH(text);
435
436        // Reached the end of the string
437        if (IS_END_OF_STRING(glyph)) {
438            break;
439        }
440
441        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
442
443        glyphsCount++;
444    }
445}
446
447void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
448        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
449        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
450    if (numGlyphs == 0 || text == NULL || len == 0) {
451        return;
452    }
453
454    static RenderGlyph gRenderGlyph[] = {
455            &android::uirenderer::Font::drawCachedGlyph,
456            &android::uirenderer::Font::drawCachedGlyphBitmap,
457            &android::uirenderer::Font::measureCachedGlyph
458    };
459    RenderGlyph render = gRenderGlyph[mode];
460
461    text += start;
462    int glyphsCount = 0;
463
464    if (CC_LIKELY(positions == NULL)) {
465        SkFixed prevRsbDelta = 0;
466
467        float penX = x + 0.5f;
468        int penY = y;
469
470        while (glyphsCount < numGlyphs) {
471            glyph_t glyph = GET_GLYPH(text);
472
473            // Reached the end of the string
474            if (IS_END_OF_STRING(glyph)) {
475                break;
476            }
477
478            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
479            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
480            prevRsbDelta = cachedGlyph->mRsbDelta;
481
482            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
483            if (cachedGlyph->mIsValid) {
484                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
485                        bitmap, bitmapW, bitmapH, bounds, positions);
486            }
487
488            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
489
490            glyphsCount++;
491        }
492    } else {
493        const SkPaint::Align align = paint->getTextAlign();
494
495        // This is for renderPosText()
496        while (glyphsCount < numGlyphs) {
497            glyph_t glyph = GET_GLYPH(text);
498
499            // Reached the end of the string
500            if (IS_END_OF_STRING(glyph)) {
501                break;
502            }
503
504            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
505
506            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
507            if (cachedGlyph->mIsValid) {
508                int penX = x + positions[(glyphsCount << 1)];
509                int penY = y + positions[(glyphsCount << 1) + 1];
510
511                switch (align) {
512                    case SkPaint::kRight_Align:
513                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
514                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
515                        break;
516                    case SkPaint::kCenter_Align:
517                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
518                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
519                    default:
520                        break;
521                }
522
523                (*this.*render)(cachedGlyph, penX, penY,
524                        bitmap, bitmapW, bitmapH, bounds, positions);
525            }
526
527            glyphsCount++;
528        }
529    }
530}
531
532void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
533    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
534    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
535    glyph->mBitmapLeft = skiaGlyph.fLeft;
536    glyph->mBitmapTop = skiaGlyph.fTop;
537    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
538    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
539
540    uint32_t startX = 0;
541    uint32_t startY = 0;
542
543    // Get the bitmap for the glyph
544    paint->findImage(skiaGlyph);
545    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
546
547    if (!glyph->mIsValid) {
548        return;
549    }
550
551    uint32_t endX = startX + skiaGlyph.fWidth;
552    uint32_t endY = startY + skiaGlyph.fHeight;
553
554    glyph->mStartX = startX;
555    glyph->mStartY = startY;
556    glyph->mBitmapWidth = skiaGlyph.fWidth;
557    glyph->mBitmapHeight = skiaGlyph.fHeight;
558
559    uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
560    uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
561
562    glyph->mBitmapMinU = startX / (float) cacheWidth;
563    glyph->mBitmapMinV = startY / (float) cacheHeight;
564    glyph->mBitmapMaxU = endX / (float) cacheWidth;
565    glyph->mBitmapMaxV = endY / (float) cacheHeight;
566
567    mState->mUploadTexture = true;
568}
569
570CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
571    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
572    mCachedGlyphs.add(glyph, newGlyph);
573
574    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
575    newGlyph->mGlyphIndex = skiaGlyph.fID;
576    newGlyph->mIsValid = false;
577
578    updateGlyphCache(paint, skiaGlyph, newGlyph);
579
580    return newGlyph;
581}
582
583Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
584        int flags, uint32_t italicStyle, uint32_t scaleX,
585        SkPaint::Style style, uint32_t strokeWidth) {
586    Vector<Font*> &activeFonts = state->mActiveFonts;
587
588    for (uint32_t i = 0; i < activeFonts.size(); i++) {
589        Font* font = activeFonts[i];
590        if (font->mFontId == fontId && font->mFontSize == fontSize &&
591                font->mFlags == flags && font->mItalicStyle == italicStyle &&
592                font->mScaleX == scaleX && font->mStyle == style &&
593                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
594            return font;
595        }
596    }
597
598    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
599            scaleX, style, strokeWidth);
600    activeFonts.push(newFont);
601    return newFont;
602}
603
604///////////////////////////////////////////////////////////////////////////////
605// FontRenderer
606///////////////////////////////////////////////////////////////////////////////
607
608static bool sLogFontRendererCreate = true;
609
610FontRenderer::FontRenderer() {
611    if (sLogFontRendererCreate) {
612        INIT_LOGD("Creating FontRenderer");
613    }
614
615    mGammaTable = NULL;
616    mInitialized = false;
617    mMaxNumberOfQuads = 1024;
618    mCurrentQuadIndex = 0;
619
620    mTextMeshPtr = NULL;
621    mCurrentCacheTexture = NULL;
622    mLastCacheTexture = NULL;
623
624    mLinearFiltering = false;
625
626    mIndexBufferID = 0;
627
628    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
629    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
630
631    char property[PROPERTY_VALUE_MAX];
632    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
633        if (sLogFontRendererCreate) {
634            INIT_LOGD("  Setting text cache width to %s pixels", property);
635        }
636        mSmallCacheWidth = atoi(property);
637    } else {
638        if (sLogFontRendererCreate) {
639            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
640        }
641    }
642
643    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
644        if (sLogFontRendererCreate) {
645            INIT_LOGD("  Setting text cache width to %s pixels", property);
646        }
647        mSmallCacheHeight = atoi(property);
648    } else {
649        if (sLogFontRendererCreate) {
650            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
651        }
652    }
653
654    sLogFontRendererCreate = false;
655}
656
657FontRenderer::~FontRenderer() {
658    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
659        delete mCacheTextures[i];
660    }
661    mCacheTextures.clear();
662
663    if (mInitialized) {
664        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
665        Caches::getInstance().unbindIndicesBuffer();
666        glDeleteBuffers(1, &mIndexBufferID);
667
668        delete[] mTextMeshPtr;
669    }
670
671    Vector<Font*> fontsToDereference = mActiveFonts;
672    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
673        delete fontsToDereference[i];
674    }
675}
676
677void FontRenderer::flushAllAndInvalidate() {
678    if (mCurrentQuadIndex != 0) {
679        issueDrawCommand();
680        mCurrentQuadIndex = 0;
681    }
682
683    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
684        mActiveFonts[i]->invalidateTextureCache();
685    }
686
687    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
688        mCacheTextures[i]->init();
689    }
690
691    #if DEBUG_FONT_RENDERER
692    uint16_t totalGlyphs = 0;
693    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
694        totalGlyphs += mCacheTextures[i]->mNumGlyphs;
695        // Erase caches, just as a debugging facility
696        if (mCacheTextures[i]->mTexture) {
697            memset(mCacheTextures[i]->mTexture, 0,
698                    mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
699        }
700    }
701    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
702#endif
703}
704
705void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
706    if (cacheTexture && cacheTexture->mTexture) {
707        glDeleteTextures(1, &cacheTexture->mTextureId);
708        delete[] cacheTexture->mTexture;
709        cacheTexture->mTexture = NULL;
710        cacheTexture->mTextureId = 0;
711    }
712}
713
714void FontRenderer::flushLargeCaches() {
715    // Start from 1; don't deallocate smallest/default texture
716    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
717        CacheTexture* cacheTexture = mCacheTextures[i];
718        if (cacheTexture->mTexture != NULL) {
719            cacheTexture->init();
720            for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
721                mActiveFonts[j]->invalidateTextureCache(cacheTexture);
722            }
723            deallocateTextureMemory(cacheTexture);
724        }
725    }
726}
727
728void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
729    int width = cacheTexture->mWidth;
730    int height = cacheTexture->mHeight;
731
732    cacheTexture->mTexture = new uint8_t[width * height];
733
734    if (!cacheTexture->mTextureId) {
735        glGenTextures(1, &cacheTexture->mTextureId);
736    }
737
738    Caches::getInstance().activeTexture(0);
739    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
740    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
741    // Initialize texture dimensions
742    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
743            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
744
745    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
746    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
747    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
748
749    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
750    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
751}
752
753CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
754        uint32_t* startX, uint32_t* startY) {
755    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
756        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
757            return mCacheTextures[i];
758        }
759    }
760    // Could not fit glyph into current cache textures
761    return NULL;
762}
763
764void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
765        uint32_t* retOriginX, uint32_t* retOriginY) {
766    checkInit();
767    cachedGlyph->mIsValid = false;
768    // If the glyph is too tall, don't cache it
769    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
770                mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
771        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
772                (int) glyph.fWidth, (int) glyph.fHeight);
773        return;
774    }
775
776    // Now copy the bitmap into the cache texture
777    uint32_t startX = 0;
778    uint32_t startY = 0;
779
780    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
781
782    // If the new glyph didn't fit, flush the state so far and invalidate everything
783    if (!cacheTexture) {
784        flushAllAndInvalidate();
785
786        // Try to fit it again
787        cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
788
789        // if we still don't fit, something is wrong and we shouldn't draw
790        if (!cacheTexture) {
791            return;
792        }
793    }
794
795    cachedGlyph->mCacheTexture = cacheTexture;
796
797    *retOriginX = startX;
798    *retOriginY = startY;
799
800    uint32_t endX = startX + glyph.fWidth;
801    uint32_t endY = startY + glyph.fHeight;
802
803    uint32_t cacheWidth = cacheTexture->mWidth;
804
805    if (!cacheTexture->mTexture) {
806        // Large-glyph texture memory is allocated only as needed
807        allocateTextureMemory(cacheTexture);
808    }
809
810    uint8_t* cacheBuffer = cacheTexture->mTexture;
811    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
812    unsigned int stride = glyph.rowBytes();
813
814    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
815
816    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
817        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
818        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
819    }
820
821    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
822            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
823        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
824        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
825    }
826
827    if (mGammaTable) {
828        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
829            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
830                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
831                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
832            }
833        }
834    } else {
835        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
836            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
837                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
838                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
839            }
840        }
841    }
842
843    cachedGlyph->mIsValid = true;
844}
845
846CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
847    CacheTexture* cacheTexture = new CacheTexture(width, height);
848
849    if (allocate) {
850        allocateTextureMemory(cacheTexture);
851    }
852
853    return cacheTexture;
854}
855
856void FontRenderer::initTextTexture() {
857    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
858        delete mCacheTextures[i];
859    }
860    mCacheTextures.clear();
861
862    // Next, use other, separate caches for large glyphs.
863    uint16_t maxWidth = 0;
864    if (Caches::hasInstance()) {
865        maxWidth = Caches::getInstance().maxTextureSize;
866    }
867
868    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
869        maxWidth = MAX_TEXT_CACHE_WIDTH;
870    }
871
872    mUploadTexture = false;
873    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
874    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
875    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
876    mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
877    mCurrentCacheTexture = mCacheTextures[0];
878}
879
880// Avoid having to reallocate memory and render quad by quad
881void FontRenderer::initVertexArrayBuffers() {
882    uint32_t numIndices = mMaxNumberOfQuads * 6;
883    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
884    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
885
886    // Four verts, two triangles , six indices per quad
887    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
888        int i6 = i * 6;
889        int i4 = i * 4;
890
891        indexBufferData[i6 + 0] = i4 + 0;
892        indexBufferData[i6 + 1] = i4 + 1;
893        indexBufferData[i6 + 2] = i4 + 2;
894
895        indexBufferData[i6 + 3] = i4 + 0;
896        indexBufferData[i6 + 4] = i4 + 2;
897        indexBufferData[i6 + 5] = i4 + 3;
898    }
899
900    glGenBuffers(1, &mIndexBufferID);
901    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
902    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
903
904    free(indexBufferData);
905
906    uint32_t coordSize = 2;
907    uint32_t uvSize = 2;
908    uint32_t vertsPerQuad = 4;
909    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
910    mTextMeshPtr = new float[vertexBufferSize];
911}
912
913// We don't want to allocate anything unless we actually draw text
914void FontRenderer::checkInit() {
915    if (mInitialized) {
916        return;
917    }
918
919    initTextTexture();
920    initVertexArrayBuffers();
921
922    mInitialized = true;
923}
924
925void FontRenderer::checkTextureUpdate() {
926    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
927        return;
928    }
929
930    Caches& caches = Caches::getInstance();
931    GLuint lastTextureId = 0;
932    // Iterate over all the cache textures and see which ones need to be updated
933    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
934        CacheTexture* cacheTexture = mCacheTextures[i];
935        if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
936            uint32_t xOffset = 0;
937            uint32_t width   = cacheTexture->mWidth;
938            uint32_t height  = cacheTexture->mHeight;
939            void* textureData = cacheTexture->mTexture;
940
941            if (cacheTexture->mTextureId != lastTextureId) {
942                caches.activeTexture(0);
943                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
944                lastTextureId = cacheTexture->mTextureId;
945            }
946#if DEBUG_FONT_RENDERER
947            ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
948                    i, xOffset, width, height);
949#endif
950            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
951                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
952
953            cacheTexture->mDirty = false;
954        }
955    }
956
957    caches.activeTexture(0);
958    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
959    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
960        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
961        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
962        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
963        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
964    }
965    mLastCacheTexture = mCurrentCacheTexture;
966
967    mUploadTexture = false;
968}
969
970void FontRenderer::issueDrawCommand() {
971    checkTextureUpdate();
972
973    Caches& caches = Caches::getInstance();
974    caches.bindIndicesBuffer(mIndexBufferID);
975    if (!mDrawn) {
976        float* buffer = mTextMeshPtr;
977        int offset = 2;
978
979        bool force = caches.unbindMeshBuffer();
980        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
981        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
982                buffer + offset);
983    }
984
985    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
986
987    mDrawn = true;
988}
989
990void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
991        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
992        float x4, float y4, float u4, float v4, CacheTexture* texture) {
993    if (texture != mCurrentCacheTexture) {
994        if (mCurrentQuadIndex != 0) {
995            // First, draw everything stored already which uses the previous texture
996            issueDrawCommand();
997            mCurrentQuadIndex = 0;
998        }
999        // Now use the new texture id
1000        mCurrentCacheTexture = texture;
1001    }
1002
1003    const uint32_t vertsPerQuad = 4;
1004    const uint32_t floatsPerVert = 4;
1005    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
1006
1007    (*currentPos++) = x1;
1008    (*currentPos++) = y1;
1009    (*currentPos++) = u1;
1010    (*currentPos++) = v1;
1011
1012    (*currentPos++) = x2;
1013    (*currentPos++) = y2;
1014    (*currentPos++) = u2;
1015    (*currentPos++) = v2;
1016
1017    (*currentPos++) = x3;
1018    (*currentPos++) = y3;
1019    (*currentPos++) = u3;
1020    (*currentPos++) = v3;
1021
1022    (*currentPos++) = x4;
1023    (*currentPos++) = y4;
1024    (*currentPos++) = u4;
1025    (*currentPos++) = v4;
1026
1027    mCurrentQuadIndex++;
1028}
1029
1030void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1031        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1032        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1033
1034    if (mClip &&
1035            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1036        return;
1037    }
1038
1039    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1040
1041    if (mBounds) {
1042        mBounds->left = fmin(mBounds->left, x1);
1043        mBounds->top = fmin(mBounds->top, y3);
1044        mBounds->right = fmax(mBounds->right, x3);
1045        mBounds->bottom = fmax(mBounds->bottom, y1);
1046    }
1047
1048    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1049        issueDrawCommand();
1050        mCurrentQuadIndex = 0;
1051    }
1052}
1053
1054void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1055        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1056        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1057
1058    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1059
1060    if (mBounds) {
1061        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1062        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1063        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1064        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1065    }
1066
1067    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1068        issueDrawCommand();
1069        mCurrentQuadIndex = 0;
1070    }
1071}
1072
1073void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
1074    int flags = 0;
1075    if (paint->isFakeBoldText()) {
1076        flags |= Font::kFakeBold;
1077    }
1078
1079    const float skewX = paint->getTextSkewX();
1080    uint32_t italicStyle = *(uint32_t*) &skewX;
1081    const float scaleXFloat = paint->getTextScaleX();
1082    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1083    SkPaint::Style style = paint->getStyle();
1084    const float strokeWidthFloat = paint->getStrokeWidth();
1085    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1086    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1087            scaleX, style, strokeWidth);
1088
1089}
1090
1091FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1092        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
1093    checkInit();
1094
1095    if (!mCurrentFont) {
1096        DropShadow image;
1097        image.width = 0;
1098        image.height = 0;
1099        image.image = NULL;
1100        image.penX = 0;
1101        image.penY = 0;
1102        return image;
1103    }
1104
1105    mDrawn = false;
1106    mClip = NULL;
1107    mBounds = NULL;
1108
1109    Rect bounds;
1110    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1111
1112    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1113    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1114    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1115
1116    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1117        dataBuffer[i] = 0;
1118    }
1119
1120    int penX = radius - bounds.left;
1121    int penY = radius - bounds.bottom;
1122
1123    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1124            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1125    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1126
1127    DropShadow image;
1128    image.width = paddedWidth;
1129    image.height = paddedHeight;
1130    image.image = dataBuffer;
1131    image.penX = penX;
1132    image.penY = penY;
1133
1134    return image;
1135}
1136
1137void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1138    checkInit();
1139
1140    mDrawn = false;
1141    mBounds = bounds;
1142    mClip = clip;
1143}
1144
1145void FontRenderer::finishRender() {
1146    mBounds = NULL;
1147    mClip = NULL;
1148
1149    if (mCurrentQuadIndex != 0) {
1150        issueDrawCommand();
1151        mCurrentQuadIndex = 0;
1152    }
1153}
1154
1155void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1156    int flags = 0;
1157    if (paint->isFakeBoldText()) {
1158        flags |= Font::kFakeBold;
1159    }
1160    const float skewX = paint->getTextSkewX();
1161    uint32_t italicStyle = *(uint32_t*) &skewX;
1162    const float scaleXFloat = paint->getTextScaleX();
1163    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1164    SkPaint::Style style = paint->getStyle();
1165    const float strokeWidthFloat = paint->getStrokeWidth();
1166    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1167    float fontSize = paint->getTextSize();
1168    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1169            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1170
1171    font->precache(paint, text, numGlyphs);
1172}
1173
1174bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1175        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1176    if (!mCurrentFont) {
1177        ALOGE("No font set");
1178        return false;
1179    }
1180
1181    initRender(clip, bounds);
1182    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1183    finishRender();
1184
1185    return mDrawn;
1186}
1187
1188bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1189        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1190        const float* positions, Rect* bounds) {
1191    if (!mCurrentFont) {
1192        ALOGE("No font set");
1193        return false;
1194    }
1195
1196    initRender(clip, bounds);
1197    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1198    finishRender();
1199
1200    return mDrawn;
1201}
1202
1203bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1204        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1205        float hOffset, float vOffset, Rect* bounds) {
1206    if (!mCurrentFont) {
1207        ALOGE("No font set");
1208        return false;
1209    }
1210
1211    initRender(clip, bounds);
1212    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1213    finishRender();
1214
1215    return mDrawn;
1216}
1217
1218void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1219    // Compute gaussian weights for the blur
1220    // e is the euler's number
1221    float e = 2.718281828459045f;
1222    float pi = 3.1415926535897932f;
1223    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1224    // x is of the form [-radius .. 0 .. radius]
1225    // and sigma varies with radius.
1226    // Based on some experimental radius values and sigma's
1227    // we approximately fit sigma = f(radius) as
1228    // sigma = radius * 0.3  + 0.6
1229    // The larger the radius gets, the more our gaussian blur
1230    // will resemble a box blur since with large sigma
1231    // the gaussian curve begins to lose its shape
1232    float sigma = 0.3f * (float) radius + 0.6f;
1233
1234    // Now compute the coefficints
1235    // We will store some redundant values to save some math during
1236    // the blur calculations
1237    // precompute some values
1238    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1239    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1240
1241    float normalizeFactor = 0.0f;
1242    for (int32_t r = -radius; r <= radius; r ++) {
1243        float floatR = (float) r;
1244        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1245        normalizeFactor += weights[r + radius];
1246    }
1247
1248    //Now we need to normalize the weights because all our coefficients need to add up to one
1249    normalizeFactor = 1.0f / normalizeFactor;
1250    for (int32_t r = -radius; r <= radius; r ++) {
1251        weights[r + radius] *= normalizeFactor;
1252    }
1253}
1254
1255void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1256        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1257    float blurredPixel = 0.0f;
1258    float currentPixel = 0.0f;
1259
1260    for (int32_t y = 0; y < height; y ++) {
1261
1262        const uint8_t* input = source + y * width;
1263        uint8_t* output = dest + y * width;
1264
1265        for (int32_t x = 0; x < width; x ++) {
1266            blurredPixel = 0.0f;
1267            const float* gPtr = weights;
1268            // Optimization for non-border pixels
1269            if (x > radius && x < (width - radius)) {
1270                const uint8_t *i = input + (x - radius);
1271                for (int r = -radius; r <= radius; r ++) {
1272                    currentPixel = (float) (*i);
1273                    blurredPixel += currentPixel * gPtr[0];
1274                    gPtr++;
1275                    i++;
1276                }
1277            } else {
1278                for (int32_t r = -radius; r <= radius; r ++) {
1279                    // Stepping left and right away from the pixel
1280                    int validW = x + r;
1281                    if (validW < 0) {
1282                        validW = 0;
1283                    }
1284                    if (validW > width - 1) {
1285                        validW = width - 1;
1286                    }
1287
1288                    currentPixel = (float) input[validW];
1289                    blurredPixel += currentPixel * gPtr[0];
1290                    gPtr++;
1291                }
1292            }
1293            *output = (uint8_t)blurredPixel;
1294            output ++;
1295        }
1296    }
1297}
1298
1299void FontRenderer::verticalBlur(float* weights, int32_t radius,
1300        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1301    float blurredPixel = 0.0f;
1302    float currentPixel = 0.0f;
1303
1304    for (int32_t y = 0; y < height; y ++) {
1305
1306        uint8_t* output = dest + y * width;
1307
1308        for (int32_t x = 0; x < width; x ++) {
1309            blurredPixel = 0.0f;
1310            const float* gPtr = weights;
1311            const uint8_t* input = source + x;
1312            // Optimization for non-border pixels
1313            if (y > radius && y < (height - radius)) {
1314                const uint8_t *i = input + ((y - radius) * width);
1315                for (int32_t r = -radius; r <= radius; r ++) {
1316                    currentPixel = (float)(*i);
1317                    blurredPixel += currentPixel * gPtr[0];
1318                    gPtr++;
1319                    i += width;
1320                }
1321            } else {
1322                for (int32_t r = -radius; r <= radius; r ++) {
1323                    int validH = y + r;
1324                    // Clamp to zero and width
1325                    if (validH < 0) {
1326                        validH = 0;
1327                    }
1328                    if (validH > height - 1) {
1329                        validH = height - 1;
1330                    }
1331
1332                    const uint8_t *i = input + validH * width;
1333                    currentPixel = (float) (*i);
1334                    blurredPixel += currentPixel * gPtr[0];
1335                    gPtr++;
1336                }
1337            }
1338            *output = (uint8_t) blurredPixel;
1339            output ++;
1340        }
1341    }
1342}
1343
1344
1345void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1346    float *gaussian = new float[2 * radius + 1];
1347    computeGaussianWeights(gaussian, radius);
1348
1349    uint8_t* scratch = new uint8_t[width * height];
1350
1351    horizontalBlur(gaussian, radius, image, scratch, width, height);
1352    verticalBlur(gaussian, radius, scratch, image, width, height);
1353
1354    delete[] gaussian;
1355    delete[] scratch;
1356}
1357
1358}; // namespace uirenderer
1359}; // namespace android
1360