FontRenderer.cpp revision 15bc6437f8b4cf10dba55c7638d349e7b9563f4f
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
29namespace android {
30namespace uirenderer {
31
32///////////////////////////////////////////////////////////////////////////////
33// Defines
34///////////////////////////////////////////////////////////////////////////////
35
36#define DEFAULT_TEXT_CACHE_WIDTH 1024
37#define DEFAULT_TEXT_CACHE_HEIGHT 256
38
39// We should query these values from the GL context
40#define MAX_TEXT_CACHE_WIDTH 2048
41#define MAX_TEXT_CACHE_HEIGHT 2048
42
43///////////////////////////////////////////////////////////////////////////////
44// Font
45///////////////////////////////////////////////////////////////////////////////
46
47Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
48        int flags, uint32_t italicStyle, uint32_t scaleX,
49        SkPaint::Style style, uint32_t strokeWidth) :
50        mState(state), mFontId(fontId), mFontSize(fontSize),
51        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
52        mStyle(style), mStrokeWidth(mStrokeWidth) {
53}
54
55
56Font::~Font() {
57    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
58        if (mState->mActiveFonts[ct] == this) {
59            mState->mActiveFonts.removeAt(ct);
60            break;
61        }
62    }
63
64    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
65        delete mCachedGlyphs.valueAt(i);
66    }
67}
68
69void Font::invalidateTextureCache() {
70    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
71        mCachedGlyphs.valueAt(i)->mIsValid = false;
72    }
73}
74
75void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
76    int nPenX = x + glyph->mBitmapLeft;
77    int nPenY = y + glyph->mBitmapTop;
78
79    int width = (int) glyph->mBitmapWidth;
80    int height = (int) glyph->mBitmapHeight;
81
82    if (bounds->bottom > nPenY) {
83        bounds->bottom = nPenY;
84    }
85    if (bounds->left > nPenX) {
86        bounds->left = nPenX;
87    }
88    if (bounds->right < nPenX + width) {
89        bounds->right = nPenX + width;
90    }
91    if (bounds->top < nPenY + height) {
92        bounds->top = nPenY + height;
93    }
94}
95
96void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
97    int nPenX = x + glyph->mBitmapLeft;
98    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
99
100    float u1 = glyph->mBitmapMinU;
101    float u2 = glyph->mBitmapMaxU;
102    float v1 = glyph->mBitmapMinV;
103    float v2 = glyph->mBitmapMaxV;
104
105    int width = (int) glyph->mBitmapWidth;
106    int height = (int) glyph->mBitmapHeight;
107
108    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
109            nPenX + width, nPenY, u2, v2,
110            nPenX + width, nPenY - height, u2, v1,
111            nPenX, nPenY - height, u1, v1);
112}
113
114void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
115        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
116    int nPenX = x + glyph->mBitmapLeft;
117    int nPenY = y + glyph->mBitmapTop;
118
119    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
120    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
121
122    uint32_t cacheWidth = mState->getCacheWidth();
123    const uint8_t* cacheBuffer = mState->getTextTextureData();
124
125    uint32_t cacheX = 0, cacheY = 0;
126    int32_t bX = 0, bY = 0;
127    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
128        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
129            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
130                LOGE("Skipping invalid index");
131                continue;
132            }
133            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
134            bitmap[bY * bitmapW + bX] = tempCol;
135        }
136    }
137
138}
139
140Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
141    CachedGlyphInfo* cachedGlyph = NULL;
142    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
143    if (index >= 0) {
144        cachedGlyph = mCachedGlyphs.valueAt(index);
145    } else {
146        cachedGlyph = cacheGlyph(paint, textUnit);
147    }
148
149    // Is the glyph still in texture cache?
150    if (!cachedGlyph->mIsValid) {
151        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
152        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
153    }
154
155    return cachedGlyph;
156}
157
158void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
159        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
160    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
161        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
162                bitmapW, bitmapH, NULL);
163    } else {
164        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
165                0, 0, NULL);
166    }
167}
168
169void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
170        int numGlyphs, Rect *bounds) {
171    if (bounds == NULL) {
172        LOGE("No return rectangle provided to measure text");
173        return;
174    }
175    bounds->set(1e6, -1e6, -1e6, 1e6);
176    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
177}
178
179#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
180
181void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
182        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
183        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
184    if (numGlyphs == 0 || text == NULL || len == 0) {
185        return;
186    }
187
188    float penX = x;
189    int penY = y;
190    int glyphsLeft = 1;
191    if (numGlyphs > 0) {
192        glyphsLeft = numGlyphs;
193    }
194
195    SkFixed prevRsbDelta = 0;
196    penX += 0.5f;
197
198    text += start;
199
200    while (glyphsLeft > 0) {
201        glyph_t glyph = GET_GLYPH(text);
202
203        // Reached the end of the string
204        if (IS_END_OF_STRING(glyph)) {
205            break;
206        }
207
208        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
209        penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
210        prevRsbDelta = cachedGlyph->mRsbDelta;
211
212        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
213        if (cachedGlyph->mIsValid) {
214            switch(mode) {
215            case FRAMEBUFFER:
216                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
217                break;
218            case BITMAP:
219                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
220                break;
221            case MEASURE:
222                measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
223                break;
224            }
225        }
226
227        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
228
229        // If we were given a specific number of glyphs, decrement
230        if (numGlyphs > 0) {
231            glyphsLeft--;
232        }
233    }
234}
235
236void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
237    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
238    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
239    glyph->mBitmapLeft = skiaGlyph.fLeft;
240    glyph->mBitmapTop = skiaGlyph.fTop;
241    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
242    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
243
244    uint32_t startX = 0;
245    uint32_t startY = 0;
246
247    // Get the bitmap for the glyph
248    paint->findImage(skiaGlyph);
249    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
250
251    if (!glyph->mIsValid) {
252        return;
253    }
254
255    uint32_t endX = startX + skiaGlyph.fWidth;
256    uint32_t endY = startY + skiaGlyph.fHeight;
257
258    glyph->mStartX = startX;
259    glyph->mStartY = startY;
260    glyph->mBitmapWidth = skiaGlyph.fWidth;
261    glyph->mBitmapHeight = skiaGlyph.fHeight;
262
263    uint32_t cacheWidth = mState->getCacheWidth();
264    uint32_t cacheHeight = mState->getCacheHeight();
265
266    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
267    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
268    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
269    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
270
271    mState->mUploadTexture = true;
272}
273
274Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
275    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
276    mCachedGlyphs.add(glyph, newGlyph);
277
278    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
279    newGlyph->mGlyphIndex = skiaGlyph.fID;
280    newGlyph->mIsValid = false;
281
282    updateGlyphCache(paint, skiaGlyph, newGlyph);
283
284    return newGlyph;
285}
286
287Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
288        int flags, uint32_t italicStyle, uint32_t scaleX,
289        SkPaint::Style style, uint32_t strokeWidth) {
290    Vector<Font*> &activeFonts = state->mActiveFonts;
291
292    for (uint32_t i = 0; i < activeFonts.size(); i++) {
293        Font* font = activeFonts[i];
294        if (font->mFontId == fontId && font->mFontSize == fontSize &&
295                font->mFlags == flags && font->mItalicStyle == italicStyle &&
296                font->mScaleX == scaleX && font->mStyle == style &&
297                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
298            return font;
299        }
300    }
301
302    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
303            scaleX, style, strokeWidth);
304    activeFonts.push(newFont);
305    return newFont;
306}
307
308///////////////////////////////////////////////////////////////////////////////
309// FontRenderer
310///////////////////////////////////////////////////////////////////////////////
311
312static bool sLogFontRendererCreate = true;
313
314FontRenderer::FontRenderer() {
315    if (sLogFontRendererCreate) {
316        INIT_LOGD("Creating FontRenderer");
317    }
318
319    mGammaTable = NULL;
320    mInitialized = false;
321    mMaxNumberOfQuads = 1024;
322    mCurrentQuadIndex = 0;
323    mTextureId = 0;
324
325    mTextMeshPtr = NULL;
326    mTextTexture = NULL;
327
328    mIndexBufferID = 0;
329
330    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
331    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
332
333    char property[PROPERTY_VALUE_MAX];
334    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
335        if (sLogFontRendererCreate) {
336            INIT_LOGD("  Setting text cache width to %s pixels", property);
337        }
338        mCacheWidth = atoi(property);
339    } else {
340        if (sLogFontRendererCreate) {
341            INIT_LOGD("  Using default text cache width of %i pixels", mCacheWidth);
342        }
343    }
344
345    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
346        if (sLogFontRendererCreate) {
347            INIT_LOGD("  Setting text cache width to %s pixels", property);
348        }
349        mCacheHeight = atoi(property);
350    } else {
351        if (sLogFontRendererCreate) {
352            INIT_LOGD("  Using default text cache height of %i pixels", mCacheHeight);
353        }
354    }
355
356    sLogFontRendererCreate = false;
357}
358
359FontRenderer::~FontRenderer() {
360    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
361        delete mCacheLines[i];
362    }
363    mCacheLines.clear();
364
365    if (mInitialized) {
366        delete[] mTextMeshPtr;
367        delete[] mTextTexture;
368    }
369
370    if (mTextureId) {
371        glDeleteTextures(1, &mTextureId);
372    }
373
374    Vector<Font*> fontsToDereference = mActiveFonts;
375    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
376        delete fontsToDereference[i];
377    }
378}
379
380void FontRenderer::flushAllAndInvalidate() {
381    if (mCurrentQuadIndex != 0) {
382        issueDrawCommand();
383        mCurrentQuadIndex = 0;
384    }
385    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
386        mActiveFonts[i]->invalidateTextureCache();
387    }
388    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
389        mCacheLines[i]->mCurrentCol = 0;
390    }
391}
392
393bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
394    // If the glyph is too tall, don't cache it
395    if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
396        if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
397            // Default cache not large enough for large glyphs - resize cache to
398            // max size and try again
399            flushAllAndInvalidate();
400            initTextTexture(true);
401        }
402        if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
403            LOGE("Font size to large to fit in cache. width, height = %i, %i",
404                    (int) glyph.fWidth, (int) glyph.fHeight);
405            return false;
406        }
407    }
408
409    // Now copy the bitmap into the cache texture
410    uint32_t startX = 0;
411    uint32_t startY = 0;
412
413    bool bitmapFit = false;
414    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
415        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
416        if (bitmapFit) {
417            break;
418        }
419    }
420
421    // If the new glyph didn't fit, flush the state so far and invalidate everything
422    if (!bitmapFit) {
423        flushAllAndInvalidate();
424
425        // Try to fit it again
426        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
427            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
428            if (bitmapFit) {
429                break;
430            }
431        }
432
433        // if we still don't fit, something is wrong and we shouldn't draw
434        if (!bitmapFit) {
435            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
436                    (int) glyph.fWidth, (int) glyph.fHeight);
437            return false;
438        }
439    }
440
441    *retOriginX = startX;
442    *retOriginY = startY;
443
444    uint32_t endX = startX + glyph.fWidth;
445    uint32_t endY = startY + glyph.fHeight;
446
447    uint32_t cacheWidth = mCacheWidth;
448
449    uint8_t* cacheBuffer = mTextTexture;
450    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
451    unsigned int stride = glyph.rowBytes();
452
453    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
454    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
455        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
456            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
457            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
458        }
459    }
460
461    return true;
462}
463
464void FontRenderer::initTextTexture(bool largeFonts) {
465    mCacheLines.clear();
466    if (largeFonts) {
467        mCacheWidth = MAX_TEXT_CACHE_WIDTH;
468        mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
469    }
470
471    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
472    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
473
474    mUploadTexture = false;
475
476    if (mTextureId != 0) {
477        glDeleteTextures(1, &mTextureId);
478    }
479    glGenTextures(1, &mTextureId);
480    glBindTexture(GL_TEXTURE_2D, mTextureId);
481    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
482    // Initialize texture dimensions
483    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
484            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
485
486    mLinearFiltering = false;
487    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
488    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
489
490    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
491    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
492
493    // Split up our cache texture into lines of certain widths
494    int nextLine = 0;
495    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
496    nextLine += mCacheLines.top()->mMaxHeight;
497    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
498    nextLine += mCacheLines.top()->mMaxHeight;
499    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
500    nextLine += mCacheLines.top()->mMaxHeight;
501    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
502    nextLine += mCacheLines.top()->mMaxHeight;
503    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
504    nextLine += mCacheLines.top()->mMaxHeight;
505    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
506    nextLine += mCacheLines.top()->mMaxHeight;
507    if (largeFonts) {
508        int nextSize = 76;
509        // Make several new lines with increasing font sizes
510        while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
511            mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
512            nextLine += mCacheLines.top()->mMaxHeight;
513            nextSize += 50;
514        }
515    }
516    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
517}
518
519// Avoid having to reallocate memory and render quad by quad
520void FontRenderer::initVertexArrayBuffers() {
521    uint32_t numIndices = mMaxNumberOfQuads * 6;
522    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
523    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
524
525    // Four verts, two triangles , six indices per quad
526    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
527        int i6 = i * 6;
528        int i4 = i * 4;
529
530        indexBufferData[i6 + 0] = i4 + 0;
531        indexBufferData[i6 + 1] = i4 + 1;
532        indexBufferData[i6 + 2] = i4 + 2;
533
534        indexBufferData[i6 + 3] = i4 + 0;
535        indexBufferData[i6 + 4] = i4 + 2;
536        indexBufferData[i6 + 5] = i4 + 3;
537    }
538
539    glGenBuffers(1, &mIndexBufferID);
540    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
541    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
542
543    free(indexBufferData);
544
545    uint32_t coordSize = 2;
546    uint32_t uvSize = 2;
547    uint32_t vertsPerQuad = 4;
548    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
549    mTextMeshPtr = new float[vertexBufferSize];
550}
551
552// We don't want to allocate anything unless we actually draw text
553void FontRenderer::checkInit() {
554    if (mInitialized) {
555        return;
556    }
557
558    initTextTexture();
559    initVertexArrayBuffers();
560
561    // We store a string with letters in a rough frequency of occurrence
562    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
563    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
564    mLatinPrecache += String16(",.?!()-+@;:`'");
565    mLatinPrecache += String16("0123456789");
566
567    mInitialized = true;
568}
569
570void FontRenderer::checkTextureUpdate() {
571    if (!mUploadTexture) {
572        return;
573    }
574
575    glBindTexture(GL_TEXTURE_2D, mTextureId);
576
577    // Iterate over all the cache lines and see which ones need to be updated
578    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
579        CacheTextureLine* cl = mCacheLines[i];
580        if (cl->mDirty) {
581            uint32_t xOffset = 0;
582            uint32_t yOffset = cl->mCurrentRow;
583            uint32_t width   = mCacheWidth;
584            uint32_t height  = cl->mMaxHeight;
585            void* textureData = mTextTexture + yOffset * width;
586
587            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
588                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
589
590            cl->mDirty = false;
591        }
592    }
593
594    mUploadTexture = false;
595}
596
597void FontRenderer::issueDrawCommand() {
598    checkTextureUpdate();
599
600    Caches& caches = Caches::getInstance();
601    if (!mDrawn) {
602        float* buffer = mTextMeshPtr;
603        int offset = 2;
604
605        bool force = caches.unbindMeshBuffer();
606        caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
607        caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
608                buffer + offset);
609    }
610
611    caches.bindIndicesBuffer(mIndexBufferID);
612    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
613
614    mDrawn = true;
615}
616
617void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
618        float x2, float y2, float u2, float v2,
619        float x3, float y3, float u3, float v3,
620        float x4, float y4, float u4, float v4) {
621
622    if (mClip &&
623            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
624        return;
625    }
626
627    const uint32_t vertsPerQuad = 4;
628    const uint32_t floatsPerVert = 4;
629    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
630
631    (*currentPos++) = x1;
632    (*currentPos++) = y1;
633    (*currentPos++) = u1;
634    (*currentPos++) = v1;
635
636    (*currentPos++) = x2;
637    (*currentPos++) = y2;
638    (*currentPos++) = u2;
639    (*currentPos++) = v2;
640
641    (*currentPos++) = x3;
642    (*currentPos++) = y3;
643    (*currentPos++) = u3;
644    (*currentPos++) = v3;
645
646    (*currentPos++) = x4;
647    (*currentPos++) = y4;
648    (*currentPos++) = u4;
649    (*currentPos++) = v4;
650
651    mCurrentQuadIndex++;
652
653    if (mBounds) {
654        mBounds->left = fmin(mBounds->left, x1);
655        mBounds->top = fmin(mBounds->top, y3);
656        mBounds->right = fmax(mBounds->right, x3);
657        mBounds->bottom = fmax(mBounds->bottom, y1);
658    }
659
660    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
661        issueDrawCommand();
662        mCurrentQuadIndex = 0;
663    }
664}
665
666uint32_t FontRenderer::getRemainingCacheCapacity() {
667    uint32_t remainingCapacity = 0;
668    float totalPixels = 0;
669    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
670         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
671         totalPixels += mCacheLines[i]->mMaxWidth;
672    }
673    remainingCapacity = (remainingCapacity * 100) / totalPixels;
674    return remainingCapacity;
675}
676
677void FontRenderer::precacheLatin(SkPaint* paint) {
678    // Remaining capacity is measured in %
679    uint32_t remainingCapacity = getRemainingCacheCapacity();
680    uint32_t precacheIdx = 0;
681    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
682        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
683        remainingCapacity = getRemainingCacheCapacity();
684        precacheIdx ++;
685    }
686}
687
688void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
689    uint32_t currentNumFonts = mActiveFonts.size();
690    int flags = 0;
691    if (paint->isFakeBoldText()) {
692        flags |= Font::kFakeBold;
693    }
694
695    const float skewX = paint->getTextSkewX();
696    uint32_t italicStyle = *(uint32_t*) &skewX;
697    const float scaleXFloat = paint->getTextScaleX();
698    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
699    SkPaint::Style style = paint->getStyle();
700    const float strokeWidthFloat = paint->getStrokeWidth();
701    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
702    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
703            scaleX, style, strokeWidth);
704
705    const float maxPrecacheFontSize = 40.0f;
706    bool isNewFont = currentNumFonts != mActiveFonts.size();
707
708    if (isNewFont && fontSize <= maxPrecacheFontSize) {
709        precacheLatin(paint);
710    }
711}
712
713FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
714        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
715    checkInit();
716
717    if (!mCurrentFont) {
718        DropShadow image;
719        image.width = 0;
720        image.height = 0;
721        image.image = NULL;
722        image.penX = 0;
723        image.penY = 0;
724        return image;
725    }
726
727    mClip = NULL;
728    mBounds = NULL;
729
730    Rect bounds;
731    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
732
733    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
734    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
735    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
736
737    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
738        dataBuffer[i] = 0;
739    }
740
741    int penX = radius - bounds.left;
742    int penY = radius - bounds.bottom;
743
744    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
745            dataBuffer, paddedWidth, paddedHeight);
746    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
747
748    DropShadow image;
749    image.width = paddedWidth;
750    image.height = paddedHeight;
751    image.image = dataBuffer;
752    image.penX = penX;
753    image.penY = penY;
754    return image;
755}
756
757bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
758        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
759    checkInit();
760
761    if (!mCurrentFont) {
762        LOGE("No font set");
763        return false;
764    }
765
766    mDrawn = false;
767    mBounds = bounds;
768    mClip = clip;
769
770    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
771
772    mBounds = NULL;
773    mClip = NULL;
774
775    if (mCurrentQuadIndex != 0) {
776        issueDrawCommand();
777        mCurrentQuadIndex = 0;
778    }
779
780    return mDrawn;
781}
782
783void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
784    // Compute gaussian weights for the blur
785    // e is the euler's number
786    float e = 2.718281828459045f;
787    float pi = 3.1415926535897932f;
788    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
789    // x is of the form [-radius .. 0 .. radius]
790    // and sigma varies with radius.
791    // Based on some experimental radius values and sigma's
792    // we approximately fit sigma = f(radius) as
793    // sigma = radius * 0.3  + 0.6
794    // The larger the radius gets, the more our gaussian blur
795    // will resemble a box blur since with large sigma
796    // the gaussian curve begins to lose its shape
797    float sigma = 0.3f * (float) radius + 0.6f;
798
799    // Now compute the coefficints
800    // We will store some redundant values to save some math during
801    // the blur calculations
802    // precompute some values
803    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
804    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
805
806    float normalizeFactor = 0.0f;
807    for (int32_t r = -radius; r <= radius; r ++) {
808        float floatR = (float) r;
809        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
810        normalizeFactor += weights[r + radius];
811    }
812
813    //Now we need to normalize the weights because all our coefficients need to add up to one
814    normalizeFactor = 1.0f / normalizeFactor;
815    for (int32_t r = -radius; r <= radius; r ++) {
816        weights[r + radius] *= normalizeFactor;
817    }
818}
819
820void FontRenderer::horizontalBlur(float* weights, int32_t radius,
821        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
822    float blurredPixel = 0.0f;
823    float currentPixel = 0.0f;
824
825    for (int32_t y = 0; y < height; y ++) {
826
827        const uint8_t* input = source + y * width;
828        uint8_t* output = dest + y * width;
829
830        for (int32_t x = 0; x < width; x ++) {
831            blurredPixel = 0.0f;
832            const float* gPtr = weights;
833            // Optimization for non-border pixels
834            if (x > radius && x < (width - radius)) {
835                const uint8_t *i = input + (x - radius);
836                for (int r = -radius; r <= radius; r ++) {
837                    currentPixel = (float) (*i);
838                    blurredPixel += currentPixel * gPtr[0];
839                    gPtr++;
840                    i++;
841                }
842            } else {
843                for (int32_t r = -radius; r <= radius; r ++) {
844                    // Stepping left and right away from the pixel
845                    int validW = x + r;
846                    if (validW < 0) {
847                        validW = 0;
848                    }
849                    if (validW > width - 1) {
850                        validW = width - 1;
851                    }
852
853                    currentPixel = (float) input[validW];
854                    blurredPixel += currentPixel * gPtr[0];
855                    gPtr++;
856                }
857            }
858            *output = (uint8_t)blurredPixel;
859            output ++;
860        }
861    }
862}
863
864void FontRenderer::verticalBlur(float* weights, int32_t radius,
865        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
866    float blurredPixel = 0.0f;
867    float currentPixel = 0.0f;
868
869    for (int32_t y = 0; y < height; y ++) {
870
871        uint8_t* output = dest + y * width;
872
873        for (int32_t x = 0; x < width; x ++) {
874            blurredPixel = 0.0f;
875            const float* gPtr = weights;
876            const uint8_t* input = source + x;
877            // Optimization for non-border pixels
878            if (y > radius && y < (height - radius)) {
879                const uint8_t *i = input + ((y - radius) * width);
880                for (int32_t r = -radius; r <= radius; r ++) {
881                    currentPixel = (float)(*i);
882                    blurredPixel += currentPixel * gPtr[0];
883                    gPtr++;
884                    i += width;
885                }
886            } else {
887                for (int32_t r = -radius; r <= radius; r ++) {
888                    int validH = y + r;
889                    // Clamp to zero and width
890                    if (validH < 0) {
891                        validH = 0;
892                    }
893                    if (validH > height - 1) {
894                        validH = height - 1;
895                    }
896
897                    const uint8_t *i = input + validH * width;
898                    currentPixel = (float) (*i);
899                    blurredPixel += currentPixel * gPtr[0];
900                    gPtr++;
901                }
902            }
903            *output = (uint8_t) blurredPixel;
904            output ++;
905        }
906    }
907}
908
909
910void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
911    float *gaussian = new float[2 * radius + 1];
912    computeGaussianWeights(gaussian, radius);
913
914    uint8_t* scratch = new uint8_t[width * height];
915
916    horizontalBlur(gaussian, radius, image, scratch, width, height);
917    verticalBlur(gaussian, radius, scratch, image, width, height);
918
919    delete[] gaussian;
920    delete[] scratch;
921}
922
923}; // namespace uirenderer
924}; // namespace android
925