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