FontRenderer.cpp revision e816baea651476aca4407200d4a5e629b9ab8dfa
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// CacheTextureLine
113///////////////////////////////////////////////////////////////////////////////
114
115bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
116    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
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 = mCurrentRow + 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 (mMaxHeight - glyphH >= glyphH) {
150                    // There's enough height left over to create a new CacheBlock
151                    CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
152                            mMaxHeight - glyphH);
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(CacheTextureLine *cacheLine) {
217    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
218        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
219        if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
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->mCachedTextureLine->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->mCachedTextureLine->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->mCachedTextureLine->mCacheTexture);
329}
330
331CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
332    CachedGlyphInfo* cachedGlyph = NULL;
333    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
334    if (index >= 0) {
335        cachedGlyph = mCachedGlyphs.valueAt(index);
336    } else {
337        cachedGlyph = cacheGlyph(paint, textUnit);
338    }
339
340    // Is the glyph still in texture cache?
341    if (!cachedGlyph->mIsValid) {
342        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
343        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
344    }
345
346    return cachedGlyph;
347}
348
349void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
350        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
351    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
352        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
353                bitmapW, bitmapH, NULL, NULL);
354    } else {
355        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
356                0, 0, NULL, NULL);
357    }
358}
359
360void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
361            int numGlyphs, int x, int y, const float* positions) {
362    render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
363            0, 0, NULL, positions);
364}
365
366void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
367        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
368    if (numGlyphs == 0 || text == NULL || len == 0) {
369        return;
370    }
371
372    text += start;
373
374    int glyphsCount = 0;
375    SkFixed prevRsbDelta = 0;
376
377    float penX = 0.0f;
378
379    SkPoint position;
380    SkVector tangent;
381
382    SkPathMeasure measure(*path, false);
383    float pathLength = SkScalarToFloat(measure.getLength());
384
385    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
386        float textWidth = SkScalarToFloat(paint->measureText(text, len));
387        float pathOffset = pathLength;
388        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
389            textWidth *= 0.5f;
390            pathOffset *= 0.5f;
391        }
392        penX += pathOffset - textWidth;
393    }
394
395    while (glyphsCount < numGlyphs && penX < pathLength) {
396        glyph_t glyph = GET_GLYPH(text);
397
398        if (IS_END_OF_STRING(glyph)) {
399            break;
400        }
401
402        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
403        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
404        prevRsbDelta = cachedGlyph->mRsbDelta;
405
406        if (cachedGlyph->mIsValid) {
407            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
408        }
409
410        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
411
412        glyphsCount++;
413    }
414}
415
416void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
417        int numGlyphs, Rect *bounds, const float* positions) {
418    if (bounds == NULL) {
419        ALOGE("No return rectangle provided to measure text");
420        return;
421    }
422    bounds->set(1e6, -1e6, -1e6, 1e6);
423    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
424}
425
426void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
427
428    if (numGlyphs == 0 || text == NULL) {
429        return;
430    }
431    int glyphsCount = 0;
432
433    while (glyphsCount < numGlyphs) {
434        glyph_t glyph = GET_GLYPH(text);
435
436        // Reached the end of the string
437        if (IS_END_OF_STRING(glyph)) {
438            break;
439        }
440
441        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
442
443        glyphsCount++;
444    }
445}
446
447void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
448        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
449        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
450    if (numGlyphs == 0 || text == NULL || len == 0) {
451        return;
452    }
453
454    static RenderGlyph gRenderGlyph[] = {
455            &android::uirenderer::Font::drawCachedGlyph,
456            &android::uirenderer::Font::drawCachedGlyphBitmap,
457            &android::uirenderer::Font::measureCachedGlyph
458    };
459    RenderGlyph render = gRenderGlyph[mode];
460
461    text += start;
462    int glyphsCount = 0;
463
464    if (CC_LIKELY(positions == NULL)) {
465        SkFixed prevRsbDelta = 0;
466
467        float penX = x + 0.5f;
468        int penY = y;
469
470        while (glyphsCount < numGlyphs) {
471            glyph_t glyph = GET_GLYPH(text);
472
473            // Reached the end of the string
474            if (IS_END_OF_STRING(glyph)) {
475                break;
476            }
477
478            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
479            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
480            prevRsbDelta = cachedGlyph->mRsbDelta;
481
482            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
483            if (cachedGlyph->mIsValid) {
484                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
485                        bitmap, bitmapW, bitmapH, bounds, positions);
486            }
487
488            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
489
490            glyphsCount++;
491        }
492    } else {
493        const SkPaint::Align align = paint->getTextAlign();
494
495        // This is for renderPosText()
496        while (glyphsCount < numGlyphs) {
497            glyph_t glyph = GET_GLYPH(text);
498
499            // Reached the end of the string
500            if (IS_END_OF_STRING(glyph)) {
501                break;
502            }
503
504            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
505
506            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
507            if (cachedGlyph->mIsValid) {
508                int penX = x + positions[(glyphsCount << 1)];
509                int penY = y + positions[(glyphsCount << 1) + 1];
510
511                switch (align) {
512                    case SkPaint::kRight_Align:
513                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
514                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
515                        break;
516                    case SkPaint::kCenter_Align:
517                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
518                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
519                    default:
520                        break;
521                }
522
523                (*this.*render)(cachedGlyph, penX, penY,
524                        bitmap, bitmapW, bitmapH, bounds, positions);
525            }
526
527            glyphsCount++;
528        }
529    }
530}
531
532void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
533    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
534    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
535    glyph->mBitmapLeft = skiaGlyph.fLeft;
536    glyph->mBitmapTop = skiaGlyph.fTop;
537    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
538    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
539
540    uint32_t startX = 0;
541    uint32_t startY = 0;
542
543    // Get the bitmap for the glyph
544    paint->findImage(skiaGlyph);
545    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
546
547    if (!glyph->mIsValid) {
548        return;
549    }
550
551    uint32_t endX = startX + skiaGlyph.fWidth;
552    uint32_t endY = startY + skiaGlyph.fHeight;
553
554    glyph->mStartX = startX;
555    glyph->mStartY = startY;
556    glyph->mBitmapWidth = skiaGlyph.fWidth;
557    glyph->mBitmapHeight = skiaGlyph.fHeight;
558
559    uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
560    uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
561
562    glyph->mBitmapMinU = startX / (float) cacheWidth;
563    glyph->mBitmapMinV = startY / (float) cacheHeight;
564    glyph->mBitmapMaxU = endX / (float) cacheWidth;
565    glyph->mBitmapMaxV = endY / (float) cacheHeight;
566
567    mState->mUploadTexture = true;
568}
569
570CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
571    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
572    mCachedGlyphs.add(glyph, newGlyph);
573
574    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
575    newGlyph->mGlyphIndex = skiaGlyph.fID;
576    newGlyph->mIsValid = false;
577
578    updateGlyphCache(paint, skiaGlyph, newGlyph);
579
580    return newGlyph;
581}
582
583Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
584        int flags, uint32_t italicStyle, uint32_t scaleX,
585        SkPaint::Style style, uint32_t strokeWidth) {
586    Vector<Font*> &activeFonts = state->mActiveFonts;
587
588    for (uint32_t i = 0; i < activeFonts.size(); i++) {
589        Font* font = activeFonts[i];
590        if (font->mFontId == fontId && font->mFontSize == fontSize &&
591                font->mFlags == flags && font->mItalicStyle == italicStyle &&
592                font->mScaleX == scaleX && font->mStyle == style &&
593                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
594            return font;
595        }
596    }
597
598    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
599            scaleX, style, strokeWidth);
600    activeFonts.push(newFont);
601    return newFont;
602}
603
604///////////////////////////////////////////////////////////////////////////////
605// FontRenderer
606///////////////////////////////////////////////////////////////////////////////
607
608static bool sLogFontRendererCreate = true;
609
610FontRenderer::FontRenderer() {
611    if (sLogFontRendererCreate) {
612        INIT_LOGD("Creating FontRenderer");
613    }
614
615    mGammaTable = NULL;
616    mInitialized = false;
617    mMaxNumberOfQuads = 1024;
618    mCurrentQuadIndex = 0;
619
620    mTextMeshPtr = NULL;
621    mCurrentCacheTexture = NULL;
622    mLastCacheTexture = NULL;
623    mCacheTextureSmall = NULL;
624    mCacheTexture128 = NULL;
625    mCacheTexture256 = NULL;
626    mCacheTexture512 = NULL;
627
628    mLinearFiltering = false;
629
630    mIndexBufferID = 0;
631
632    mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
633    mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
634
635    char property[PROPERTY_VALUE_MAX];
636    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
637        if (sLogFontRendererCreate) {
638            INIT_LOGD("  Setting text cache width to %s pixels", property);
639        }
640        mSmallCacheWidth = atoi(property);
641    } else {
642        if (sLogFontRendererCreate) {
643            INIT_LOGD("  Using default text cache width of %i pixels", mSmallCacheWidth);
644        }
645    }
646
647    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
648        if (sLogFontRendererCreate) {
649            INIT_LOGD("  Setting text cache width to %s pixels", property);
650        }
651        mSmallCacheHeight = atoi(property);
652    } else {
653        if (sLogFontRendererCreate) {
654            INIT_LOGD("  Using default text cache height of %i pixels", mSmallCacheHeight);
655        }
656    }
657
658    sLogFontRendererCreate = false;
659}
660
661FontRenderer::~FontRenderer() {
662    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
663        delete mCacheLines[i];
664    }
665    mCacheLines.clear();
666
667    if (mInitialized) {
668        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
669        Caches::getInstance().unbindIndicesBuffer();
670        glDeleteBuffers(1, &mIndexBufferID);
671
672        delete[] mTextMeshPtr;
673        delete mCacheTextureSmall;
674        delete mCacheTexture128;
675        delete mCacheTexture256;
676        delete mCacheTexture512;
677    }
678
679    Vector<Font*> fontsToDereference = mActiveFonts;
680    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
681        delete fontsToDereference[i];
682    }
683}
684
685void FontRenderer::flushAllAndInvalidate() {
686    if (mCurrentQuadIndex != 0) {
687        issueDrawCommand();
688        mCurrentQuadIndex = 0;
689    }
690
691    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
692        mActiveFonts[i]->invalidateTextureCache();
693    }
694
695    uint16_t totalGlyphs = 0;
696    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
697        totalGlyphs += mCacheLines[i]->mNumGlyphs;
698        mCacheLines[i]->init();
699    }
700
701#if DEBUG_FONT_RENDERER
702    ALOGD("FontRenderer: flushAllAndInvalidatel");
703    // Erase caches, just as a debugging facility
704    if (mCacheTextureSmall && mCacheTextureSmall->mTexture) {
705        memset(mCacheTextureSmall->mTexture, 0,
706                mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight);
707    }
708    if (mCacheTexture128 && mCacheTexture128->mTexture) {
709        memset(mCacheTexture128->mTexture, 0,
710                mCacheTexture128->mWidth * mCacheTexture128->mHeight);
711    }
712    if (mCacheTexture256 && mCacheTexture256->mTexture) {
713        memset(mCacheTexture256->mTexture, 0,
714                mCacheTexture256->mWidth * mCacheTexture256->mHeight);
715    }
716    if (mCacheTexture512 && mCacheTexture512->mTexture) {
717        memset(mCacheTexture512->mTexture, 0,
718                mCacheTexture512->mWidth * mCacheTexture512->mHeight);
719    }
720    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
721#endif
722}
723
724void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
725    if (cacheTexture && cacheTexture->mTexture) {
726        glDeleteTextures(1, &cacheTexture->mTextureId);
727        delete[] cacheTexture->mTexture;
728        cacheTexture->mTexture = NULL;
729        cacheTexture->mTextureId = 0;
730    }
731}
732
733void FontRenderer::flushLargeCaches() {
734    if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
735            (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
736            (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
737        // Typical case; no large glyph caches allocated
738        return;
739    }
740
741    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
742        CacheTextureLine* cacheLine = mCacheLines[i];
743        if ((cacheLine->mCacheTexture == mCacheTexture128 ||
744                cacheLine->mCacheTexture == mCacheTexture256 ||
745                cacheLine->mCacheTexture == mCacheTexture512) &&
746                cacheLine->mCacheTexture->mTexture != NULL) {
747#if DEBUG_FONT_RENDERER
748            if (cacheLine->mCacheTexture == mCacheTexture128) {
749                ALOGD("flushing cacheTexture128");
750            } else if (cacheLine->mCacheTexture == mCacheTexture256) {
751                ALOGD("flushing cacheTexture256");
752            } else {
753                ALOGD("flushing cacheTexture512");
754            }
755#endif
756            cacheLine->init();
757            for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
758                mActiveFonts[i]->invalidateTextureCache(cacheLine);
759            }
760        }
761    }
762
763    deallocateTextureMemory(mCacheTexture128);
764    deallocateTextureMemory(mCacheTexture256);
765    deallocateTextureMemory(mCacheTexture512);
766}
767
768void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
769    int width = cacheTexture->mWidth;
770    int height = cacheTexture->mHeight;
771
772    cacheTexture->mTexture = new uint8_t[width * height];
773
774    if (!cacheTexture->mTextureId) {
775        glGenTextures(1, &cacheTexture->mTextureId);
776    }
777
778    Caches::getInstance().activeTexture(0);
779    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
780    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
781    // Initialize texture dimensions
782    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
783            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
784
785    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
786    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
787    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
788
789    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
790    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
791}
792
793void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
794        uint32_t* retOriginX, uint32_t* retOriginY) {
795    cachedGlyph->mIsValid = false;
796    // If the glyph is too tall, don't cache it
797    if (mCacheLines.size() == 0 ||
798        glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
799        if (mCacheLines.size() != 0) {
800            ALOGE("Font size too large to fit in cache. width, height = %i, %i",
801                    (int) glyph.fWidth, (int) glyph.fHeight);
802        }
803        return;
804    }
805
806    // Now copy the bitmap into the cache texture
807    uint32_t startX = 0;
808    uint32_t startY = 0;
809
810    bool bitmapFit = false;
811    CacheTextureLine *cacheLine;
812    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
813        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
814        if (bitmapFit) {
815            cacheLine = mCacheLines[i];
816            break;
817        }
818    }
819
820    // If the new glyph didn't fit, flush the state so far and invalidate everything
821    if (!bitmapFit) {
822        flushAllAndInvalidate();
823
824        // Try to fit it again
825        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
826            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
827            if (bitmapFit) {
828                cacheLine = mCacheLines[i];
829                break;
830            }
831        }
832
833        // if we still don't fit, something is wrong and we shouldn't draw
834        if (!bitmapFit) {
835            return;
836        }
837    }
838
839    cachedGlyph->mCachedTextureLine = cacheLine;
840
841    *retOriginX = startX;
842    *retOriginY = startY;
843
844    uint32_t endX = startX + glyph.fWidth;
845    uint32_t endY = startY + glyph.fHeight;
846
847    uint32_t cacheWidth = cacheLine->mMaxWidth;
848
849    CacheTexture* cacheTexture = cacheLine->mCacheTexture;
850    if (!cacheTexture->mTexture) {
851        // Large-glyph texture memory is allocated only as needed
852        allocateTextureMemory(cacheTexture);
853    }
854
855    uint8_t* cacheBuffer = cacheTexture->mTexture;
856    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
857    unsigned int stride = glyph.rowBytes();
858
859    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
860
861    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
862        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
863        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
864    }
865
866    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
867            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
868        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
869        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
870    }
871
872    if (mGammaTable) {
873        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
874            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
875                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
876                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
877            }
878        }
879    } else {
880        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
881            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
882                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
883                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
884            }
885        }
886    }
887
888    cachedGlyph->mIsValid = true;
889}
890
891CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
892    CacheTexture* cacheTexture = new CacheTexture(width, height);
893
894    if (allocate) {
895        allocateTextureMemory(cacheTexture);
896    }
897
898    return cacheTexture;
899}
900
901void FontRenderer::initTextTexture() {
902    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
903        delete mCacheLines[i];
904    }
905    mCacheLines.clear();
906
907    if (mCacheTextureSmall) {
908        delete mCacheTextureSmall;
909        delete mCacheTexture128;
910        delete mCacheTexture256;
911        delete mCacheTexture512;
912    }
913
914    // Next, use other, separate caches for large glyphs.
915    uint16_t maxWidth = 0;
916    if (Caches::hasInstance()) {
917        maxWidth = Caches::getInstance().maxTextureSize;
918    }
919
920    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
921        maxWidth = MAX_TEXT_CACHE_WIDTH;
922    }
923
924    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
925    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
926    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
927    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
928    mCurrentCacheTexture = mCacheTextureSmall;
929
930    mUploadTexture = false;
931    // Split up our default cache texture into lines of certain widths
932    int nextLine = 0;
933    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall));
934    nextLine += mCacheLines.top()->mMaxHeight;
935    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
936    nextLine += mCacheLines.top()->mMaxHeight;
937    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
938    nextLine += mCacheLines.top()->mMaxHeight;
939    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
940    nextLine += mCacheLines.top()->mMaxHeight;
941    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
942    nextLine += mCacheLines.top()->mMaxHeight;
943    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall));
944    nextLine += mCacheLines.top()->mMaxHeight;
945    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
946            nextLine, mCacheTextureSmall));
947
948    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
949    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128));
950    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128));
951    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256));
952    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512));
953}
954
955// Avoid having to reallocate memory and render quad by quad
956void FontRenderer::initVertexArrayBuffers() {
957    uint32_t numIndices = mMaxNumberOfQuads * 6;
958    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
959    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
960
961    // Four verts, two triangles , six indices per quad
962    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
963        int i6 = i * 6;
964        int i4 = i * 4;
965
966        indexBufferData[i6 + 0] = i4 + 0;
967        indexBufferData[i6 + 1] = i4 + 1;
968        indexBufferData[i6 + 2] = i4 + 2;
969
970        indexBufferData[i6 + 3] = i4 + 0;
971        indexBufferData[i6 + 4] = i4 + 2;
972        indexBufferData[i6 + 5] = i4 + 3;
973    }
974
975    glGenBuffers(1, &mIndexBufferID);
976    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
977    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
978
979    free(indexBufferData);
980
981    uint32_t coordSize = 2;
982    uint32_t uvSize = 2;
983    uint32_t vertsPerQuad = 4;
984    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
985    mTextMeshPtr = new float[vertexBufferSize];
986}
987
988// We don't want to allocate anything unless we actually draw text
989void FontRenderer::checkInit() {
990    if (mInitialized) {
991        return;
992    }
993
994    initTextTexture();
995    initVertexArrayBuffers();
996
997    mInitialized = true;
998}
999
1000void FontRenderer::checkTextureUpdate() {
1001    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
1002        return;
1003    }
1004
1005    Caches& caches = Caches::getInstance();
1006    GLuint lastTextureId = 0;
1007    // Iterate over all the cache lines and see which ones need to be updated
1008    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
1009        CacheTextureLine* cl = mCacheLines[i];
1010        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
1011            CacheTexture* cacheTexture = cl->mCacheTexture;
1012            uint32_t xOffset = 0;
1013            uint32_t yOffset = cl->mCurrentRow;
1014            uint32_t width   = cl->mMaxWidth;
1015            uint32_t height  = cl->mMaxHeight;
1016            void* textureData = cacheTexture->mTexture + (yOffset * width);
1017
1018            if (cacheTexture->mTextureId != lastTextureId) {
1019                caches.activeTexture(0);
1020                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
1021                lastTextureId = cacheTexture->mTextureId;
1022            }
1023#if DEBUG_FONT_RENDERER
1024            ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i,
1025                    xOffset, yOffset, width, height);
1026#endif
1027            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
1028                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
1029
1030            cl->mDirty = false;
1031        }
1032    }
1033
1034    caches.activeTexture(0);
1035    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
1036    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
1037        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
1038        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
1039        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
1040        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
1041    }
1042    mLastCacheTexture = mCurrentCacheTexture;
1043
1044    mUploadTexture = false;
1045}
1046
1047void FontRenderer::issueDrawCommand() {
1048    checkTextureUpdate();
1049
1050    Caches& caches = Caches::getInstance();
1051    caches.bindIndicesBuffer(mIndexBufferID);
1052    if (!mDrawn) {
1053        float* buffer = mTextMeshPtr;
1054        int offset = 2;
1055
1056        bool force = caches.unbindMeshBuffer();
1057        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
1058        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
1059                buffer + offset);
1060    }
1061
1062    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
1063
1064    mDrawn = true;
1065}
1066
1067void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
1068        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1069        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1070    if (texture != mCurrentCacheTexture) {
1071        if (mCurrentQuadIndex != 0) {
1072            // First, draw everything stored already which uses the previous texture
1073            issueDrawCommand();
1074            mCurrentQuadIndex = 0;
1075        }
1076        // Now use the new texture id
1077        mCurrentCacheTexture = texture;
1078    }
1079
1080    const uint32_t vertsPerQuad = 4;
1081    const uint32_t floatsPerVert = 4;
1082    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
1083
1084    (*currentPos++) = x1;
1085    (*currentPos++) = y1;
1086    (*currentPos++) = u1;
1087    (*currentPos++) = v1;
1088
1089    (*currentPos++) = x2;
1090    (*currentPos++) = y2;
1091    (*currentPos++) = u2;
1092    (*currentPos++) = v2;
1093
1094    (*currentPos++) = x3;
1095    (*currentPos++) = y3;
1096    (*currentPos++) = u3;
1097    (*currentPos++) = v3;
1098
1099    (*currentPos++) = x4;
1100    (*currentPos++) = y4;
1101    (*currentPos++) = u4;
1102    (*currentPos++) = v4;
1103
1104    mCurrentQuadIndex++;
1105}
1106
1107void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1108        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1109        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1110
1111    if (mClip &&
1112            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1113        return;
1114    }
1115
1116    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1117
1118    if (mBounds) {
1119        mBounds->left = fmin(mBounds->left, x1);
1120        mBounds->top = fmin(mBounds->top, y3);
1121        mBounds->right = fmax(mBounds->right, x3);
1122        mBounds->bottom = fmax(mBounds->bottom, y1);
1123    }
1124
1125    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1126        issueDrawCommand();
1127        mCurrentQuadIndex = 0;
1128    }
1129}
1130
1131void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1132        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1133        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1134
1135    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1136
1137    if (mBounds) {
1138        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1139        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1140        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1141        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1142    }
1143
1144    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1145        issueDrawCommand();
1146        mCurrentQuadIndex = 0;
1147    }
1148}
1149
1150void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
1151    int flags = 0;
1152    if (paint->isFakeBoldText()) {
1153        flags |= Font::kFakeBold;
1154    }
1155
1156    const float skewX = paint->getTextSkewX();
1157    uint32_t italicStyle = *(uint32_t*) &skewX;
1158    const float scaleXFloat = paint->getTextScaleX();
1159    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1160    SkPaint::Style style = paint->getStyle();
1161    const float strokeWidthFloat = paint->getStrokeWidth();
1162    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1163    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1164            scaleX, style, strokeWidth);
1165
1166}
1167
1168FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1169        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
1170    checkInit();
1171
1172    if (!mCurrentFont) {
1173        DropShadow image;
1174        image.width = 0;
1175        image.height = 0;
1176        image.image = NULL;
1177        image.penX = 0;
1178        image.penY = 0;
1179        return image;
1180    }
1181
1182    mDrawn = false;
1183    mClip = NULL;
1184    mBounds = NULL;
1185
1186    Rect bounds;
1187    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1188
1189    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1190    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1191    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1192
1193    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1194        dataBuffer[i] = 0;
1195    }
1196
1197    int penX = radius - bounds.left;
1198    int penY = radius - bounds.bottom;
1199
1200    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1201            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1202    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1203
1204    DropShadow image;
1205    image.width = paddedWidth;
1206    image.height = paddedHeight;
1207    image.image = dataBuffer;
1208    image.penX = penX;
1209    image.penY = penY;
1210
1211    return image;
1212}
1213
1214void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1215    checkInit();
1216
1217    mDrawn = false;
1218    mBounds = bounds;
1219    mClip = clip;
1220}
1221
1222void FontRenderer::finishRender() {
1223    mBounds = NULL;
1224    mClip = NULL;
1225
1226    if (mCurrentQuadIndex != 0) {
1227        issueDrawCommand();
1228        mCurrentQuadIndex = 0;
1229    }
1230}
1231
1232void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1233    int flags = 0;
1234    if (paint->isFakeBoldText()) {
1235        flags |= Font::kFakeBold;
1236    }
1237    const float skewX = paint->getTextSkewX();
1238    uint32_t italicStyle = *(uint32_t*) &skewX;
1239    const float scaleXFloat = paint->getTextScaleX();
1240    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1241    SkPaint::Style style = paint->getStyle();
1242    const float strokeWidthFloat = paint->getStrokeWidth();
1243    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1244    float fontSize = paint->getTextSize();
1245    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1246            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1247
1248    font->precache(paint, text, numGlyphs);
1249}
1250
1251bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1252        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1253    if (!mCurrentFont) {
1254        ALOGE("No font set");
1255        return false;
1256    }
1257
1258    initRender(clip, bounds);
1259    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1260    finishRender();
1261
1262    return mDrawn;
1263}
1264
1265bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1266        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1267        const float* positions, Rect* bounds) {
1268    if (!mCurrentFont) {
1269        ALOGE("No font set");
1270        return false;
1271    }
1272
1273    initRender(clip, bounds);
1274    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1275    finishRender();
1276
1277    return mDrawn;
1278}
1279
1280bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1281        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1282        float hOffset, float vOffset, Rect* bounds) {
1283    if (!mCurrentFont) {
1284        ALOGE("No font set");
1285        return false;
1286    }
1287
1288    initRender(clip, bounds);
1289    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1290    finishRender();
1291
1292    return mDrawn;
1293}
1294
1295void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1296    // Compute gaussian weights for the blur
1297    // e is the euler's number
1298    float e = 2.718281828459045f;
1299    float pi = 3.1415926535897932f;
1300    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1301    // x is of the form [-radius .. 0 .. radius]
1302    // and sigma varies with radius.
1303    // Based on some experimental radius values and sigma's
1304    // we approximately fit sigma = f(radius) as
1305    // sigma = radius * 0.3  + 0.6
1306    // The larger the radius gets, the more our gaussian blur
1307    // will resemble a box blur since with large sigma
1308    // the gaussian curve begins to lose its shape
1309    float sigma = 0.3f * (float) radius + 0.6f;
1310
1311    // Now compute the coefficints
1312    // We will store some redundant values to save some math during
1313    // the blur calculations
1314    // precompute some values
1315    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1316    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1317
1318    float normalizeFactor = 0.0f;
1319    for (int32_t r = -radius; r <= radius; r ++) {
1320        float floatR = (float) r;
1321        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1322        normalizeFactor += weights[r + radius];
1323    }
1324
1325    //Now we need to normalize the weights because all our coefficients need to add up to one
1326    normalizeFactor = 1.0f / normalizeFactor;
1327    for (int32_t r = -radius; r <= radius; r ++) {
1328        weights[r + radius] *= normalizeFactor;
1329    }
1330}
1331
1332void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1333        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1334    float blurredPixel = 0.0f;
1335    float currentPixel = 0.0f;
1336
1337    for (int32_t y = 0; y < height; y ++) {
1338
1339        const uint8_t* input = source + y * width;
1340        uint8_t* output = dest + y * width;
1341
1342        for (int32_t x = 0; x < width; x ++) {
1343            blurredPixel = 0.0f;
1344            const float* gPtr = weights;
1345            // Optimization for non-border pixels
1346            if (x > radius && x < (width - radius)) {
1347                const uint8_t *i = input + (x - radius);
1348                for (int r = -radius; r <= radius; r ++) {
1349                    currentPixel = (float) (*i);
1350                    blurredPixel += currentPixel * gPtr[0];
1351                    gPtr++;
1352                    i++;
1353                }
1354            } else {
1355                for (int32_t r = -radius; r <= radius; r ++) {
1356                    // Stepping left and right away from the pixel
1357                    int validW = x + r;
1358                    if (validW < 0) {
1359                        validW = 0;
1360                    }
1361                    if (validW > width - 1) {
1362                        validW = width - 1;
1363                    }
1364
1365                    currentPixel = (float) input[validW];
1366                    blurredPixel += currentPixel * gPtr[0];
1367                    gPtr++;
1368                }
1369            }
1370            *output = (uint8_t)blurredPixel;
1371            output ++;
1372        }
1373    }
1374}
1375
1376void FontRenderer::verticalBlur(float* weights, int32_t radius,
1377        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1378    float blurredPixel = 0.0f;
1379    float currentPixel = 0.0f;
1380
1381    for (int32_t y = 0; y < height; y ++) {
1382
1383        uint8_t* output = dest + y * width;
1384
1385        for (int32_t x = 0; x < width; x ++) {
1386            blurredPixel = 0.0f;
1387            const float* gPtr = weights;
1388            const uint8_t* input = source + x;
1389            // Optimization for non-border pixels
1390            if (y > radius && y < (height - radius)) {
1391                const uint8_t *i = input + ((y - radius) * width);
1392                for (int32_t r = -radius; r <= radius; r ++) {
1393                    currentPixel = (float)(*i);
1394                    blurredPixel += currentPixel * gPtr[0];
1395                    gPtr++;
1396                    i += width;
1397                }
1398            } else {
1399                for (int32_t r = -radius; r <= radius; r ++) {
1400                    int validH = y + r;
1401                    // Clamp to zero and width
1402                    if (validH < 0) {
1403                        validH = 0;
1404                    }
1405                    if (validH > height - 1) {
1406                        validH = height - 1;
1407                    }
1408
1409                    const uint8_t *i = input + validH * width;
1410                    currentPixel = (float) (*i);
1411                    blurredPixel += currentPixel * gPtr[0];
1412                    gPtr++;
1413                }
1414            }
1415            *output = (uint8_t) blurredPixel;
1416            output ++;
1417        }
1418    }
1419}
1420
1421
1422void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1423    float *gaussian = new float[2 * radius + 1];
1424    computeGaussianWeights(gaussian, radius);
1425
1426    uint8_t* scratch = new uint8_t[width * height];
1427
1428    horizontalBlur(gaussian, radius, image, scratch, width, height);
1429    verticalBlur(gaussian, radius, scratch, image, width, height);
1430
1431    delete[] gaussian;
1432    delete[] scratch;
1433}
1434
1435}; // namespace uirenderer
1436}; // namespace android
1437