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