FontRenderer.cpp revision 2efd5c5886d9acf747bc92f888d731ed558aabcc
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    // Erase caches, just as a debugging facility
703    if (mCacheTextureSmall && mCacheTextureSmall->mTexture) {
704        memset(mCacheTextureSmall->mTexture, 0,
705                mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight);
706    }
707    if (mCacheTexture128 && mCacheTexture128->mTexture) {
708        memset(mCacheTexture128->mTexture, 0,
709                mCacheTexture128->mWidth * mCacheTexture128->mHeight);
710    }
711    if (mCacheTexture256 && mCacheTexture256->mTexture) {
712        memset(mCacheTexture256->mTexture, 0,
713                mCacheTexture256->mWidth * mCacheTexture256->mHeight);
714    }
715    if (mCacheTexture512 && mCacheTexture512->mTexture) {
716        memset(mCacheTexture512->mTexture, 0,
717                mCacheTexture512->mWidth * mCacheTexture512->mHeight);
718    }
719    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
720#endif
721}
722
723void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
724    if (cacheTexture && cacheTexture->mTexture) {
725        glDeleteTextures(1, &cacheTexture->mTextureId);
726        delete[] cacheTexture->mTexture;
727        cacheTexture->mTexture = NULL;
728        cacheTexture->mTextureId = 0;
729    }
730}
731
732void FontRenderer::flushLargeCaches() {
733    if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
734            (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
735            (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
736        // Typical case; no large glyph caches allocated
737        return;
738    }
739
740    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
741        CacheTextureLine* cacheLine = mCacheLines[i];
742        if ((cacheLine->mCacheTexture == mCacheTexture128 ||
743                cacheLine->mCacheTexture == mCacheTexture256 ||
744                cacheLine->mCacheTexture == mCacheTexture512) &&
745                cacheLine->mCacheTexture->mTexture != NULL) {
746#if DEBUG_FONT_RENDERER
747            if (cacheLine->mCacheTexture == mCacheTexture128) {
748                ALOGD("flushing cacheTexture128");
749            } else if (cacheLine->mCacheTexture == mCacheTexture256) {
750                ALOGD("flushing cacheTexture256");
751            } else {
752                ALOGD("flushing cacheTexture512");
753            }
754#endif
755            cacheLine->init();
756            for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
757                mActiveFonts[i]->invalidateTextureCache(cacheLine);
758            }
759        }
760    }
761
762    deallocateTextureMemory(mCacheTexture128);
763    deallocateTextureMemory(mCacheTexture256);
764    deallocateTextureMemory(mCacheTexture512);
765}
766
767void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
768    int width = cacheTexture->mWidth;
769    int height = cacheTexture->mHeight;
770
771    cacheTexture->mTexture = new uint8_t[width * height];
772
773    if (!cacheTexture->mTextureId) {
774        glGenTextures(1, &cacheTexture->mTextureId);
775    }
776
777    Caches::getInstance().activeTexture(0);
778    glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
779    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
780    // Initialize texture dimensions
781    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
782            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
783
784    const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
785    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
786    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
787
788    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
789    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
790}
791
792void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
793        uint32_t* retOriginX, uint32_t* retOriginY) {
794    checkInit();
795    cachedGlyph->mIsValid = false;
796    // If the glyph is too tall, don't cache it
797    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
798        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
799                (int) glyph.fWidth, (int) glyph.fHeight);
800        return;
801    }
802
803    // Now copy the bitmap into the cache texture
804    uint32_t startX = 0;
805    uint32_t startY = 0;
806
807    bool bitmapFit = false;
808    CacheTextureLine *cacheLine;
809    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
810        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
811        if (bitmapFit) {
812            cacheLine = mCacheLines[i];
813            break;
814        }
815    }
816
817    // If the new glyph didn't fit, flush the state so far and invalidate everything
818    if (!bitmapFit) {
819        flushAllAndInvalidate();
820
821        // Try to fit it again
822        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
823            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
824            if (bitmapFit) {
825                cacheLine = mCacheLines[i];
826                break;
827            }
828        }
829
830        // if we still don't fit, something is wrong and we shouldn't draw
831        if (!bitmapFit) {
832            return;
833        }
834    }
835
836    cachedGlyph->mCachedTextureLine = cacheLine;
837
838    *retOriginX = startX;
839    *retOriginY = startY;
840
841    uint32_t endX = startX + glyph.fWidth;
842    uint32_t endY = startY + glyph.fHeight;
843
844    uint32_t cacheWidth = cacheLine->mMaxWidth;
845
846    CacheTexture* cacheTexture = cacheLine->mCacheTexture;
847    if (!cacheTexture->mTexture) {
848        // Large-glyph texture memory is allocated only as needed
849        allocateTextureMemory(cacheTexture);
850    }
851
852    uint8_t* cacheBuffer = cacheTexture->mTexture;
853    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
854    unsigned int stride = glyph.rowBytes();
855
856    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
857
858    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
859        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
860        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
861    }
862
863    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
864            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
865        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
866        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
867    }
868
869    if (mGammaTable) {
870        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
871            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
872                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
873                cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
874            }
875        }
876    } else {
877        for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
878            for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
879                uint8_t tempCol = bitmapBuffer[bY * stride + bX];
880                cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
881            }
882        }
883    }
884
885    cachedGlyph->mIsValid = true;
886}
887
888CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
889    CacheTexture* cacheTexture = new CacheTexture(width, height);
890
891    if (allocate) {
892        allocateTextureMemory(cacheTexture);
893    }
894
895    return cacheTexture;
896}
897
898void FontRenderer::initTextTexture() {
899    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
900        delete mCacheLines[i];
901    }
902    mCacheLines.clear();
903
904    if (mCacheTextureSmall) {
905        delete mCacheTextureSmall;
906        delete mCacheTexture128;
907        delete mCacheTexture256;
908        delete mCacheTexture512;
909    }
910
911    // Next, use other, separate caches for large glyphs.
912    uint16_t maxWidth = 0;
913    if (Caches::hasInstance()) {
914        maxWidth = Caches::getInstance().maxTextureSize;
915    }
916
917    if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
918        maxWidth = MAX_TEXT_CACHE_WIDTH;
919    }
920
921    mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
922    mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
923    mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
924    mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
925    mCurrentCacheTexture = mCacheTextureSmall;
926
927    mUploadTexture = false;
928    // Split up our default cache texture into lines of certain widths
929    int nextLine = 0;
930    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall));
931    nextLine += mCacheLines.top()->mMaxHeight;
932    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
933    nextLine += mCacheLines.top()->mMaxHeight;
934    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
935    nextLine += mCacheLines.top()->mMaxHeight;
936    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
937    nextLine += mCacheLines.top()->mMaxHeight;
938    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
939    nextLine += mCacheLines.top()->mMaxHeight;
940    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall));
941    nextLine += mCacheLines.top()->mMaxHeight;
942    mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
943            nextLine, mCacheTextureSmall));
944
945    //  The first cache is split into 2 lines of height 128, the rest have just one cache line.
946    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128));
947    mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128));
948    mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256));
949    mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512));
950}
951
952// Avoid having to reallocate memory and render quad by quad
953void FontRenderer::initVertexArrayBuffers() {
954    uint32_t numIndices = mMaxNumberOfQuads * 6;
955    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
956    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
957
958    // Four verts, two triangles , six indices per quad
959    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
960        int i6 = i * 6;
961        int i4 = i * 4;
962
963        indexBufferData[i6 + 0] = i4 + 0;
964        indexBufferData[i6 + 1] = i4 + 1;
965        indexBufferData[i6 + 2] = i4 + 2;
966
967        indexBufferData[i6 + 3] = i4 + 0;
968        indexBufferData[i6 + 4] = i4 + 2;
969        indexBufferData[i6 + 5] = i4 + 3;
970    }
971
972    glGenBuffers(1, &mIndexBufferID);
973    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
974    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
975
976    free(indexBufferData);
977
978    uint32_t coordSize = 2;
979    uint32_t uvSize = 2;
980    uint32_t vertsPerQuad = 4;
981    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
982    mTextMeshPtr = new float[vertexBufferSize];
983}
984
985// We don't want to allocate anything unless we actually draw text
986void FontRenderer::checkInit() {
987    if (mInitialized) {
988        return;
989    }
990
991    initTextTexture();
992    initVertexArrayBuffers();
993
994    mInitialized = true;
995}
996
997void FontRenderer::checkTextureUpdate() {
998    if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
999        return;
1000    }
1001
1002    Caches& caches = Caches::getInstance();
1003    GLuint lastTextureId = 0;
1004    // Iterate over all the cache lines and see which ones need to be updated
1005    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
1006        CacheTextureLine* cl = mCacheLines[i];
1007        if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
1008            CacheTexture* cacheTexture = cl->mCacheTexture;
1009            uint32_t xOffset = 0;
1010            uint32_t yOffset = cl->mCurrentRow;
1011            uint32_t width   = cl->mMaxWidth;
1012            uint32_t height  = cl->mMaxHeight;
1013            void* textureData = cacheTexture->mTexture + (yOffset * width);
1014
1015            if (cacheTexture->mTextureId != lastTextureId) {
1016                caches.activeTexture(0);
1017                glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
1018                lastTextureId = cacheTexture->mTextureId;
1019            }
1020#if DEBUG_FONT_RENDERER
1021            ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i,
1022                    xOffset, yOffset, width, height);
1023#endif
1024            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
1025                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
1026
1027            cl->mDirty = false;
1028        }
1029    }
1030
1031    caches.activeTexture(0);
1032    glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
1033    if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
1034        const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
1035        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
1036        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
1037        mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
1038    }
1039    mLastCacheTexture = mCurrentCacheTexture;
1040
1041    mUploadTexture = false;
1042}
1043
1044void FontRenderer::issueDrawCommand() {
1045    checkTextureUpdate();
1046
1047    Caches& caches = Caches::getInstance();
1048    caches.bindIndicesBuffer(mIndexBufferID);
1049    if (!mDrawn) {
1050        float* buffer = mTextMeshPtr;
1051        int offset = 2;
1052
1053        bool force = caches.unbindMeshBuffer();
1054        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
1055        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
1056                buffer + offset);
1057    }
1058
1059    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
1060
1061    mDrawn = true;
1062}
1063
1064void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
1065        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1066        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1067    if (texture != mCurrentCacheTexture) {
1068        if (mCurrentQuadIndex != 0) {
1069            // First, draw everything stored already which uses the previous texture
1070            issueDrawCommand();
1071            mCurrentQuadIndex = 0;
1072        }
1073        // Now use the new texture id
1074        mCurrentCacheTexture = texture;
1075    }
1076
1077    const uint32_t vertsPerQuad = 4;
1078    const uint32_t floatsPerVert = 4;
1079    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
1080
1081    (*currentPos++) = x1;
1082    (*currentPos++) = y1;
1083    (*currentPos++) = u1;
1084    (*currentPos++) = v1;
1085
1086    (*currentPos++) = x2;
1087    (*currentPos++) = y2;
1088    (*currentPos++) = u2;
1089    (*currentPos++) = v2;
1090
1091    (*currentPos++) = x3;
1092    (*currentPos++) = y3;
1093    (*currentPos++) = u3;
1094    (*currentPos++) = v3;
1095
1096    (*currentPos++) = x4;
1097    (*currentPos++) = y4;
1098    (*currentPos++) = u4;
1099    (*currentPos++) = v4;
1100
1101    mCurrentQuadIndex++;
1102}
1103
1104void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
1105        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1106        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1107
1108    if (mClip &&
1109            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
1110        return;
1111    }
1112
1113    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1114
1115    if (mBounds) {
1116        mBounds->left = fmin(mBounds->left, x1);
1117        mBounds->top = fmin(mBounds->top, y3);
1118        mBounds->right = fmax(mBounds->right, x3);
1119        mBounds->bottom = fmax(mBounds->bottom, y1);
1120    }
1121
1122    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1123        issueDrawCommand();
1124        mCurrentQuadIndex = 0;
1125    }
1126}
1127
1128void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
1129        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
1130        float x4, float y4, float u4, float v4, CacheTexture* texture) {
1131
1132    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
1133
1134    if (mBounds) {
1135        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
1136        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
1137        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
1138        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
1139    }
1140
1141    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
1142        issueDrawCommand();
1143        mCurrentQuadIndex = 0;
1144    }
1145}
1146
1147void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
1148    int flags = 0;
1149    if (paint->isFakeBoldText()) {
1150        flags |= Font::kFakeBold;
1151    }
1152
1153    const float skewX = paint->getTextSkewX();
1154    uint32_t italicStyle = *(uint32_t*) &skewX;
1155    const float scaleXFloat = paint->getTextScaleX();
1156    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1157    SkPaint::Style style = paint->getStyle();
1158    const float strokeWidthFloat = paint->getStrokeWidth();
1159    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1160    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
1161            scaleX, style, strokeWidth);
1162
1163}
1164
1165FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
1166        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
1167    checkInit();
1168
1169    if (!mCurrentFont) {
1170        DropShadow image;
1171        image.width = 0;
1172        image.height = 0;
1173        image.image = NULL;
1174        image.penX = 0;
1175        image.penY = 0;
1176        return image;
1177    }
1178
1179    mDrawn = false;
1180    mClip = NULL;
1181    mBounds = NULL;
1182
1183    Rect bounds;
1184    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
1185
1186    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
1187    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
1188    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
1189
1190    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
1191        dataBuffer[i] = 0;
1192    }
1193
1194    int penX = radius - bounds.left;
1195    int penY = radius - bounds.bottom;
1196
1197    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
1198            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
1199    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
1200
1201    DropShadow image;
1202    image.width = paddedWidth;
1203    image.height = paddedHeight;
1204    image.image = dataBuffer;
1205    image.penX = penX;
1206    image.penY = penY;
1207
1208    return image;
1209}
1210
1211void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
1212    checkInit();
1213
1214    mDrawn = false;
1215    mBounds = bounds;
1216    mClip = clip;
1217}
1218
1219void FontRenderer::finishRender() {
1220    mBounds = NULL;
1221    mClip = NULL;
1222
1223    if (mCurrentQuadIndex != 0) {
1224        issueDrawCommand();
1225        mCurrentQuadIndex = 0;
1226    }
1227}
1228
1229void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
1230    int flags = 0;
1231    if (paint->isFakeBoldText()) {
1232        flags |= Font::kFakeBold;
1233    }
1234    const float skewX = paint->getTextSkewX();
1235    uint32_t italicStyle = *(uint32_t*) &skewX;
1236    const float scaleXFloat = paint->getTextScaleX();
1237    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
1238    SkPaint::Style style = paint->getStyle();
1239    const float strokeWidthFloat = paint->getStrokeWidth();
1240    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
1241    float fontSize = paint->getTextSize();
1242    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
1243            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
1244
1245    font->precache(paint, text, numGlyphs);
1246}
1247
1248bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
1249        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
1250    if (!mCurrentFont) {
1251        ALOGE("No font set");
1252        return false;
1253    }
1254
1255    initRender(clip, bounds);
1256    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
1257    finishRender();
1258
1259    return mDrawn;
1260}
1261
1262bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
1263        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
1264        const float* positions, Rect* bounds) {
1265    if (!mCurrentFont) {
1266        ALOGE("No font set");
1267        return false;
1268    }
1269
1270    initRender(clip, bounds);
1271    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
1272    finishRender();
1273
1274    return mDrawn;
1275}
1276
1277bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
1278        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
1279        float hOffset, float vOffset, Rect* bounds) {
1280    if (!mCurrentFont) {
1281        ALOGE("No font set");
1282        return false;
1283    }
1284
1285    initRender(clip, bounds);
1286    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
1287    finishRender();
1288
1289    return mDrawn;
1290}
1291
1292void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
1293    // Compute gaussian weights for the blur
1294    // e is the euler's number
1295    float e = 2.718281828459045f;
1296    float pi = 3.1415926535897932f;
1297    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
1298    // x is of the form [-radius .. 0 .. radius]
1299    // and sigma varies with radius.
1300    // Based on some experimental radius values and sigma's
1301    // we approximately fit sigma = f(radius) as
1302    // sigma = radius * 0.3  + 0.6
1303    // The larger the radius gets, the more our gaussian blur
1304    // will resemble a box blur since with large sigma
1305    // the gaussian curve begins to lose its shape
1306    float sigma = 0.3f * (float) radius + 0.6f;
1307
1308    // Now compute the coefficints
1309    // We will store some redundant values to save some math during
1310    // the blur calculations
1311    // precompute some values
1312    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
1313    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
1314
1315    float normalizeFactor = 0.0f;
1316    for (int32_t r = -radius; r <= radius; r ++) {
1317        float floatR = (float) r;
1318        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
1319        normalizeFactor += weights[r + radius];
1320    }
1321
1322    //Now we need to normalize the weights because all our coefficients need to add up to one
1323    normalizeFactor = 1.0f / normalizeFactor;
1324    for (int32_t r = -radius; r <= radius; r ++) {
1325        weights[r + radius] *= normalizeFactor;
1326    }
1327}
1328
1329void FontRenderer::horizontalBlur(float* weights, int32_t radius,
1330        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1331    float blurredPixel = 0.0f;
1332    float currentPixel = 0.0f;
1333
1334    for (int32_t y = 0; y < height; y ++) {
1335
1336        const uint8_t* input = source + y * width;
1337        uint8_t* output = dest + y * width;
1338
1339        for (int32_t x = 0; x < width; x ++) {
1340            blurredPixel = 0.0f;
1341            const float* gPtr = weights;
1342            // Optimization for non-border pixels
1343            if (x > radius && x < (width - radius)) {
1344                const uint8_t *i = input + (x - radius);
1345                for (int r = -radius; r <= radius; r ++) {
1346                    currentPixel = (float) (*i);
1347                    blurredPixel += currentPixel * gPtr[0];
1348                    gPtr++;
1349                    i++;
1350                }
1351            } else {
1352                for (int32_t r = -radius; r <= radius; r ++) {
1353                    // Stepping left and right away from the pixel
1354                    int validW = x + r;
1355                    if (validW < 0) {
1356                        validW = 0;
1357                    }
1358                    if (validW > width - 1) {
1359                        validW = width - 1;
1360                    }
1361
1362                    currentPixel = (float) input[validW];
1363                    blurredPixel += currentPixel * gPtr[0];
1364                    gPtr++;
1365                }
1366            }
1367            *output = (uint8_t)blurredPixel;
1368            output ++;
1369        }
1370    }
1371}
1372
1373void FontRenderer::verticalBlur(float* weights, int32_t radius,
1374        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
1375    float blurredPixel = 0.0f;
1376    float currentPixel = 0.0f;
1377
1378    for (int32_t y = 0; y < height; y ++) {
1379
1380        uint8_t* output = dest + y * width;
1381
1382        for (int32_t x = 0; x < width; x ++) {
1383            blurredPixel = 0.0f;
1384            const float* gPtr = weights;
1385            const uint8_t* input = source + x;
1386            // Optimization for non-border pixels
1387            if (y > radius && y < (height - radius)) {
1388                const uint8_t *i = input + ((y - radius) * width);
1389                for (int32_t r = -radius; r <= radius; r ++) {
1390                    currentPixel = (float)(*i);
1391                    blurredPixel += currentPixel * gPtr[0];
1392                    gPtr++;
1393                    i += width;
1394                }
1395            } else {
1396                for (int32_t r = -radius; r <= radius; r ++) {
1397                    int validH = y + r;
1398                    // Clamp to zero and width
1399                    if (validH < 0) {
1400                        validH = 0;
1401                    }
1402                    if (validH > height - 1) {
1403                        validH = height - 1;
1404                    }
1405
1406                    const uint8_t *i = input + validH * width;
1407                    currentPixel = (float) (*i);
1408                    blurredPixel += currentPixel * gPtr[0];
1409                    gPtr++;
1410                }
1411            }
1412            *output = (uint8_t) blurredPixel;
1413            output ++;
1414        }
1415    }
1416}
1417
1418
1419void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
1420    float *gaussian = new float[2 * radius + 1];
1421    computeGaussianWeights(gaussian, radius);
1422
1423    uint8_t* scratch = new uint8_t[width * height];
1424
1425    horizontalBlur(gaussian, radius, image, scratch, width, height);
1426    verticalBlur(gaussian, radius, scratch, image, width, height);
1427
1428    delete[] gaussian;
1429    delete[] scratch;
1430}
1431
1432}; // namespace uirenderer
1433}; // namespace android
1434