FontRenderer.cpp revision f942cf10e04567f6b9456f6258e29c803b8bfb41
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, bool precaching) {
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, precaching);
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, precaching);
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, true);
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        bool precaching) {
534    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
535    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
536    glyph->mBitmapLeft = skiaGlyph.fLeft;
537    glyph->mBitmapTop = skiaGlyph.fTop;
538    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
539    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
540
541    uint32_t startX = 0;
542    uint32_t startY = 0;
543
544    // Get the bitmap for the glyph
545    paint->findImage(skiaGlyph);
546    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
547
548    if (!glyph->mIsValid) {
549        return;
550    }
551
552    uint32_t endX = startX + skiaGlyph.fWidth;
553    uint32_t endY = startY + skiaGlyph.fHeight;
554
555    glyph->mStartX = startX;
556    glyph->mStartY = startY;
557    glyph->mBitmapWidth = skiaGlyph.fWidth;
558    glyph->mBitmapHeight = skiaGlyph.fHeight;
559
560    uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
561    uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
562
563    glyph->mBitmapMinU = startX / (float) cacheWidth;
564    glyph->mBitmapMinV = startY / (float) cacheHeight;
565    glyph->mBitmapMaxU = endX / (float) cacheWidth;
566    glyph->mBitmapMaxV = endY / (float) cacheHeight;
567
568    mState->mUploadTexture = true;
569}
570
571CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
572    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
573    mCachedGlyphs.add(glyph, newGlyph);
574
575    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
576    newGlyph->mGlyphIndex = skiaGlyph.fID;
577    newGlyph->mIsValid = false;
578
579    updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
580
581    return newGlyph;
582}
583
584Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
585        int flags, uint32_t italicStyle, uint32_t scaleX,
586        SkPaint::Style style, uint32_t strokeWidth) {
587    Vector<Font*> &activeFonts = state->mActiveFonts;
588
589    for (uint32_t i = 0; i < activeFonts.size(); i++) {
590        Font* font = activeFonts[i];
591        if (font->mFontId == fontId && font->mFontSize == fontSize &&
592                font->mFlags == flags && font->mItalicStyle == italicStyle &&
593                font->mScaleX == scaleX && font->mStyle == style &&
594                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
595            return font;
596        }
597    }
598
599    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
600            scaleX, style, strokeWidth);
601    activeFonts.push(newFont);
602    return newFont;
603}
604
605///////////////////////////////////////////////////////////////////////////////
606// FontRenderer
607///////////////////////////////////////////////////////////////////////////////
608
609static bool sLogFontRendererCreate = true;
610
611FontRenderer::FontRenderer() {
612    if (sLogFontRendererCreate) {
613        INIT_LOGD("Creating FontRenderer");
614    }
615
616    mGammaTable = NULL;
617    mInitialized = false;
618    mMaxNumberOfQuads = 1024;
619    mCurrentQuadIndex = 0;
620
621    mTextMeshPtr = NULL;
622    mCurrentCacheTexture = NULL;
623    mLastCacheTexture = NULL;
624
625    mLinearFiltering = false;
626
627    mIndexBufferID = 0;
628
629    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
630    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
631
632    char property[PROPERTY_VALUE_MAX];
633    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
634        if (sLogFontRendererCreate) {
635            INIT_LOGD("  Setting text cache width to %s pixels", property);
636        }
637        mSmallCacheWidth = atoi(property);
638    } else {
639        if (sLogFontRendererCreate) {
640            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
641        }
642    }
643
644    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
645        if (sLogFontRendererCreate) {
646            INIT_LOGD("  Setting text cache width to %s pixels", property);
647        }
648        mSmallCacheHeight = atoi(property);
649    } else {
650        if (sLogFontRendererCreate) {
651            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
652        }
653    }
654
655    sLogFontRendererCreate = false;
656}
657
658FontRenderer::~FontRenderer() {
659    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
660        delete mCacheTextures[i];
661    }
662    mCacheTextures.clear();
663
664    if (mInitialized) {
665        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
666        Caches::getInstance().unbindIndicesBuffer();
667        glDeleteBuffers(1, &mIndexBufferID);
668
669        delete[] mTextMeshPtr;
670    }
671
672    Vector<Font*> fontsToDereference = mActiveFonts;
673    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
674        delete fontsToDereference[i];
675    }
676}
677
678void FontRenderer::flushAllAndInvalidate() {
679    if (mCurrentQuadIndex != 0) {
680        issueDrawCommand();
681        mCurrentQuadIndex = 0;
682    }
683
684    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
685        mActiveFonts[i]->invalidateTextureCache();
686    }
687
688    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
689        mCacheTextures[i]->init();
690    }
691
692    #if DEBUG_FONT_RENDERER
693    uint16_t totalGlyphs = 0;
694    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
695        totalGlyphs += mCacheTextures[i]->mNumGlyphs;
696        // Erase caches, just as a debugging facility
697        if (mCacheTextures[i]->mTexture) {
698            memset(mCacheTextures[i]->mTexture, 0,
699                    mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
700        }
701    }
702    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
703#endif
704}
705
706void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
707    if (cacheTexture && cacheTexture->mTexture) {
708        glDeleteTextures(1, &cacheTexture->mTextureId);
709        delete[] cacheTexture->mTexture;
710        cacheTexture->mTexture = NULL;
711        cacheTexture->mTextureId = 0;
712    }
713}
714
715void FontRenderer::flushLargeCaches() {
716    // Start from 1; don't deallocate smallest/default texture
717    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
718        CacheTexture* cacheTexture = mCacheTextures[i];
719        if (cacheTexture->mTexture != NULL) {
720            cacheTexture->init();
721            for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
722                mActiveFonts[j]->invalidateTextureCache(cacheTexture);
723            }
724            deallocateTextureMemory(cacheTexture);
725        }
726    }
727}
728
729void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
730    int width = cacheTexture->mWidth;
731    int height = cacheTexture->mHeight;
732
733    cacheTexture->mTexture = new uint8_t[width * height];
734
735    if (!cacheTexture->mTextureId) {
736        glGenTextures(1, &cacheTexture->mTextureId);
737    }
738
739    Caches::getInstance().activeTexture(0);
740    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
741    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
742    // Initialize texture dimensions
743    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
744            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
745
746    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
747    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
748    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
749
750    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
751    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
752}
753
754CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
755        uint32_t* startX, uint32_t* startY) {
756    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
757        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
758            return mCacheTextures[i];
759        }
760    }
761    // Could not fit glyph into current cache textures
762    return NULL;
763}
764
765void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
766        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
767    checkInit();
768    cachedGlyph->mIsValid = false;
769    // If the glyph is too tall, don't cache it
770    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
771                mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
772        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
773                (int) glyph.fWidth, (int) glyph.fHeight);
774        return;
775    }
776
777    // Now copy the bitmap into the cache texture
778    uint32_t startX = 0;
779    uint32_t startY = 0;
780
781    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
782
783    if (!cacheTexture) {
784        if (!precaching) {
785            // If the new glyph didn't fit and we are not just trying to precache it,
786            // clear out the cache and try again
787            flushAllAndInvalidate();
788            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
789        }
790
791        if (!cacheTexture) {
792            // either the glyph didn't fit or we're precaching and will cache it when we draw
793            return;
794        }
795    }
796
797    cachedGlyph->mCacheTexture = cacheTexture;
798
799    *retOriginX = startX;
800    *retOriginY = startY;
801
802    uint32_t endX = startX + glyph.fWidth;
803    uint32_t endY = startY + glyph.fHeight;
804
805    uint32_t cacheWidth = cacheTexture->mWidth;
806
807    if (!cacheTexture->mTexture) {
808        // Large-glyph texture memory is allocated only as needed
809        allocateTextureMemory(cacheTexture);
810    }
811
812    uint8_t* cacheBuffer = cacheTexture->mTexture;
813    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
814    unsigned int stride = glyph.rowBytes();
815
816    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
817
818    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
819        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
820        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
821    }
822
823    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
824            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
825        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
826        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
827    }
828
829    if (mGammaTable) {
830        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
831            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
832                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
833                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
834            }
835        }
836    } else {
837        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
838            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
839                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
840                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
841            }
842        }
843    }
844
845    cachedGlyph->mIsValid = true;
846}
847
848CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
849    CacheTexture* cacheTexture = new CacheTexture(width, height);
850
851    if (allocate) {
852        allocateTextureMemory(cacheTexture);
853    }
854
855    return cacheTexture;
856}
857
858void FontRenderer::initTextTexture() {
859    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
860        delete mCacheTextures[i];
861    }
862    mCacheTextures.clear();
863
864    // Next, use other, separate caches for large glyphs.
865    uint16_t maxWidth = 0;
866    if (Caches::hasInstance()) {
867        maxWidth = Caches::getInstance().maxTextureSize;
868    }
869
870    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
871        maxWidth = MAX_TEXT_CACHE_WIDTH;
872    }
873
874    mUploadTexture = false;
875    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
876    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
877    mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
878    mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
879    mCurrentCacheTexture = mCacheTextures[0];
880}
881
882// Avoid having to reallocate memory and render quad by quad
883void FontRenderer::initVertexArrayBuffers() {
884    uint32_t numIndices = mMaxNumberOfQuads * 6;
885    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
886    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
887
888    // Four verts, two triangles , six indices per quad
889    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
890        int i6 = i * 6;
891        int i4 = i * 4;
892
893        indexBufferData[i6 + 0] = i4 + 0;
894        indexBufferData[i6 + 1] = i4 + 1;
895        indexBufferData[i6 + 2] = i4 + 2;
896
897        indexBufferData[i6 + 3] = i4 + 0;
898        indexBufferData[i6 + 4] = i4 + 2;
899        indexBufferData[i6 + 5] = i4 + 3;
900    }
901
902    glGenBuffers(1, &mIndexBufferID);
903    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
904    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
905
906    free(indexBufferData);
907
908    uint32_t coordSize = 2;
909    uint32_t uvSize = 2;
910    uint32_t vertsPerQuad = 4;
911    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
912    mTextMeshPtr = new float[vertexBufferSize];
913}
914
915// We don't want to allocate anything unless we actually draw text
916void FontRenderer::checkInit() {
917    if (mInitialized) {
918        return;
919    }
920
921    initTextTexture();
922    initVertexArrayBuffers();
923
924    mInitialized = true;
925}
926
927void FontRenderer::checkTextureUpdate() {
928    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
929        return;
930    }
931
932    Caches& caches = Caches::getInstance();
933    GLuint lastTextureId = 0;
934    // Iterate over all the cache textures and see which ones need to be updated
935    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
936        CacheTexture* cacheTexture = mCacheTextures[i];
937        if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
938            uint32_t xOffset = 0;
939            uint32_t width   = cacheTexture->mWidth;
940            uint32_t height  = cacheTexture->mHeight;
941            void* textureData = cacheTexture->mTexture;
942
943            if (cacheTexture->mTextureId != lastTextureId) {
944                caches.activeTexture(0);
945                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
946                lastTextureId = cacheTexture->mTextureId;
947            }
948#if DEBUG_FONT_RENDERER
949            ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
950                    i, xOffset, width, height);
951#endif
952            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
953                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
954
955            cacheTexture->mDirty = false;
956        }
957    }
958
959    caches.activeTexture(0);
960    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
961    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
962        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
963        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
964        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
965        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
966    }
967    mLastCacheTexture = mCurrentCacheTexture;
968
969    mUploadTexture = false;
970}
971
972void FontRenderer::issueDrawCommand() {
973    checkTextureUpdate();
974
975    Caches& caches = Caches::getInstance();
976    caches.bindIndicesBuffer(mIndexBufferID);
977    if (!mDrawn) {
978        float* buffer = mTextMeshPtr;
979        int offset = 2;
980
981        bool force = caches.unbindMeshBuffer();
982        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
983        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
984                buffer + offset);
985    }
986
987    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
988
989    mDrawn = true;
990}
991
992void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
993        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
994        float x4, float y4, float u4, float v4, CacheTexture* texture) {
995    if (texture != mCurrentCacheTexture) {
996        if (mCurrentQuadIndex != 0) {
997            // First, draw everything stored already which uses the previous texture
998            issueDrawCommand();
999            mCurrentQuadIndex = 0;
1000        }
1001        // Now use the new texture id
1002        mCurrentCacheTexture = texture;
1003    }
1004
1005    const uint32_t vertsPerQuad = 4;
1006    const uint32_t floatsPerVert = 4;
1007    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
1008
1009    (*currentPos++) = x1;
1010    (*currentPos++) = y1;
1011    (*currentPos++) = u1;
1012    (*currentPos++) = v1;
1013
1014    (*currentPos++) = x2;
1015    (*currentPos++) = y2;
1016    (*currentPos++) = u2;
1017    (*currentPos++) = v2;
1018
1019    (*currentPos++) = x3;
1020    (*currentPos++) = y3;
1021    (*currentPos++) = u3;
1022    (*currentPos++) = v3;
1023
1024    (*currentPos++) = x4;
1025    (*currentPos++) = y4;
1026    (*currentPos++) = u4;
1027    (*currentPos++) = v4;
1028
1029    mCurrentQuadIndex++;
1030}
1031
1032void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1033        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1034        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1035
1036    if (mClip &&
1037            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1038        return;
1039    }
1040
1041    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1042
1043    if (mBounds) {
1044        mBounds->left = fmin(mBounds->left, x1);
1045        mBounds->top = fmin(mBounds->top, y3);
1046        mBounds->right = fmax(mBounds->right, x3);
1047        mBounds->bottom = fmax(mBounds->bottom, y1);
1048    }
1049
1050    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1051        issueDrawCommand();
1052        mCurrentQuadIndex = 0;
1053    }
1054}
1055
1056void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1057        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1058        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1059
1060    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1061
1062    if (mBounds) {
1063        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1064        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1065        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1066        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1067    }
1068
1069    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1070        issueDrawCommand();
1071        mCurrentQuadIndex = 0;
1072    }
1073}
1074
1075void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
1076    int flags = 0;
1077    if (paint->isFakeBoldText()) {
1078        flags |= Font::kFakeBold;
1079    }
1080
1081    const float skewX = paint->getTextSkewX();
1082    uint32_t italicStyle = *(uint32_t*) &skewX;
1083    const float scaleXFloat = paint->getTextScaleX();
1084    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1085    SkPaint::Style style = paint->getStyle();
1086    const float strokeWidthFloat = paint->getStrokeWidth();
1087    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1088    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1089            scaleX, style, strokeWidth);
1090
1091}
1092
1093FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1094        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
1095    checkInit();
1096
1097    if (!mCurrentFont) {
1098        DropShadow image;
1099        image.width = 0;
1100        image.height = 0;
1101        image.image = NULL;
1102        image.penX = 0;
1103        image.penY = 0;
1104        return image;
1105    }
1106
1107    mDrawn = false;
1108    mClip = NULL;
1109    mBounds = NULL;
1110
1111    Rect bounds;
1112    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1113
1114    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1115    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1116    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1117
1118    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1119        dataBuffer[i] = 0;
1120    }
1121
1122    int penX = radius - bounds.left;
1123    int penY = radius - bounds.bottom;
1124
1125    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1126            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1127    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1128
1129    DropShadow image;
1130    image.width = paddedWidth;
1131    image.height = paddedHeight;
1132    image.image = dataBuffer;
1133    image.penX = penX;
1134    image.penY = penY;
1135
1136    return image;
1137}
1138
1139void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1140    checkInit();
1141
1142    mDrawn = false;
1143    mBounds = bounds;
1144    mClip = clip;
1145}
1146
1147void FontRenderer::finishRender() {
1148    mBounds = NULL;
1149    mClip = NULL;
1150
1151    if (mCurrentQuadIndex != 0) {
1152        issueDrawCommand();
1153        mCurrentQuadIndex = 0;
1154    }
1155}
1156
1157void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1158    int flags = 0;
1159    if (paint->isFakeBoldText()) {
1160        flags |= Font::kFakeBold;
1161    }
1162    const float skewX = paint->getTextSkewX();
1163    uint32_t italicStyle = *(uint32_t*) &skewX;
1164    const float scaleXFloat = paint->getTextScaleX();
1165    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1166    SkPaint::Style style = paint->getStyle();
1167    const float strokeWidthFloat = paint->getStrokeWidth();
1168    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1169    float fontSize = paint->getTextSize();
1170    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1171            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1172
1173    font->precache(paint, text, numGlyphs);
1174}
1175
1176bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1177        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1178    if (!mCurrentFont) {
1179        ALOGE("No font set");
1180        return false;
1181    }
1182
1183    initRender(clip, bounds);
1184    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1185    finishRender();
1186
1187    return mDrawn;
1188}
1189
1190bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1191        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1192        const float* positions, Rect* bounds) {
1193    if (!mCurrentFont) {
1194        ALOGE("No font set");
1195        return false;
1196    }
1197
1198    initRender(clip, bounds);
1199    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1200    finishRender();
1201
1202    return mDrawn;
1203}
1204
1205bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1206        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1207        float hOffset, float vOffset, Rect* bounds) {
1208    if (!mCurrentFont) {
1209        ALOGE("No font set");
1210        return false;
1211    }
1212
1213    initRender(clip, bounds);
1214    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1215    finishRender();
1216
1217    return mDrawn;
1218}
1219
1220void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1221    // Compute gaussian weights for the blur
1222    // e is the euler's number
1223    float e = 2.718281828459045f;
1224    float pi = 3.1415926535897932f;
1225    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1226    // x is of the form [-radius .. 0 .. radius]
1227    // and sigma varies with radius.
1228    // Based on some experimental radius values and sigma's
1229    // we approximately fit sigma = f(radius) as
1230    // sigma = radius * 0.3  + 0.6
1231    // The larger the radius gets, the more our gaussian blur
1232    // will resemble a box blur since with large sigma
1233    // the gaussian curve begins to lose its shape
1234    float sigma = 0.3f * (float) radius + 0.6f;
1235
1236    // Now compute the coefficints
1237    // We will store some redundant values to save some math during
1238    // the blur calculations
1239    // precompute some values
1240    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1241    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1242
1243    float normalizeFactor = 0.0f;
1244    for (int32_t r = -radius; r <= radius; r ++) {
1245        float floatR = (float) r;
1246        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1247        normalizeFactor += weights[r + radius];
1248    }
1249
1250    //Now we need to normalize the weights because all our coefficients need to add up to one
1251    normalizeFactor = 1.0f / normalizeFactor;
1252    for (int32_t r = -radius; r <= radius; r ++) {
1253        weights[r + radius] *= normalizeFactor;
1254    }
1255}
1256
1257void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1258        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1259    float blurredPixel = 0.0f;
1260    float currentPixel = 0.0f;
1261
1262    for (int32_t y = 0; y < height; y ++) {
1263
1264        const uint8_t* input = source + y * width;
1265        uint8_t* output = dest + y * width;
1266
1267        for (int32_t x = 0; x < width; x ++) {
1268            blurredPixel = 0.0f;
1269            const float* gPtr = weights;
1270            // Optimization for non-border pixels
1271            if (x > radius && x < (width - radius)) {
1272                const uint8_t *i = input + (x - radius);
1273                for (int r = -radius; r <= radius; r ++) {
1274                    currentPixel = (float) (*i);
1275                    blurredPixel += currentPixel * gPtr[0];
1276                    gPtr++;
1277                    i++;
1278                }
1279            } else {
1280                for (int32_t r = -radius; r <= radius; r ++) {
1281                    // Stepping left and right away from the pixel
1282                    int validW = x + r;
1283                    if (validW < 0) {
1284                        validW = 0;
1285                    }
1286                    if (validW > width - 1) {
1287                        validW = width - 1;
1288                    }
1289
1290                    currentPixel = (float) input[validW];
1291                    blurredPixel += currentPixel * gPtr[0];
1292                    gPtr++;
1293                }
1294            }
1295            *output = (uint8_t)blurredPixel;
1296            output ++;
1297        }
1298    }
1299}
1300
1301void FontRenderer::verticalBlur(float* weights, int32_t radius,
1302        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1303    float blurredPixel = 0.0f;
1304    float currentPixel = 0.0f;
1305
1306    for (int32_t y = 0; y < height; y ++) {
1307
1308        uint8_t* output = dest + y * width;
1309
1310        for (int32_t x = 0; x < width; x ++) {
1311            blurredPixel = 0.0f;
1312            const float* gPtr = weights;
1313            const uint8_t* input = source + x;
1314            // Optimization for non-border pixels
1315            if (y > radius && y < (height - radius)) {
1316                const uint8_t *i = input + ((y - radius) * width);
1317                for (int32_t r = -radius; r <= radius; r ++) {
1318                    currentPixel = (float)(*i);
1319                    blurredPixel += currentPixel * gPtr[0];
1320                    gPtr++;
1321                    i += width;
1322                }
1323            } else {
1324                for (int32_t r = -radius; r <= radius; r ++) {
1325                    int validH = y + r;
1326                    // Clamp to zero and width
1327                    if (validH < 0) {
1328                        validH = 0;
1329                    }
1330                    if (validH > height - 1) {
1331                        validH = height - 1;
1332                    }
1333
1334                    const uint8_t *i = input + validH * width;
1335                    currentPixel = (float) (*i);
1336                    blurredPixel += currentPixel * gPtr[0];
1337                    gPtr++;
1338                }
1339            }
1340            *output = (uint8_t) blurredPixel;
1341            output ++;
1342        }
1343    }
1344}
1345
1346
1347void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1348    float *gaussian = new float[2 * radius + 1];
1349    computeGaussianWeights(gaussian, radius);
1350
1351    uint8_t* scratch = new uint8_t[width * height];
1352
1353    horizontalBlur(gaussian, radius, image, scratch, width, height);
1354    verticalBlur(gaussian, radius, scratch, image, width, height);
1355
1356    delete[] gaussian;
1357    delete[] scratch;
1358}
1359
1360}; // namespace uirenderer
1361}; // namespace android
1362