FontRenderer.cpp revision 5a6d3a4c0311318e6f1d4ee06aa47606e055a81b
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, 0, u1, v2,
108            nPenX + width, nPenY, 0, u2, v2,
109            nPenX + width, nPenY - height, 0, u2, v1,
110            nPenX, nPenY - height, 0, 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}
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    mPositionAttrSlot = -1;
330    mTexcoordAttrSlot = -1;
331
332    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
333    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
334
335    char property[PROPERTY_VALUE_MAX];
336    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
337        if (sLogFontRendererCreate) {
338            INIT_LOGD("  Setting text cache width to %s pixels", property);
339        }
340        mCacheWidth = atoi(property);
341    } else {
342        if (sLogFontRendererCreate) {
343            INIT_LOGD("  Using default text cache width of %i pixels", mCacheWidth);
344        }
345    }
346
347    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
348        if (sLogFontRendererCreate) {
349            INIT_LOGD("  Setting text cache width to %s pixels", property);
350        }
351        mCacheHeight = atoi(property);
352    } else {
353        if (sLogFontRendererCreate) {
354            INIT_LOGD("  Using default text cache height of %i pixels", mCacheHeight);
355        }
356    }
357
358    sLogFontRendererCreate = false;
359}
360
361FontRenderer::~FontRenderer() {
362    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
363        delete mCacheLines[i];
364    }
365    mCacheLines.clear();
366
367    if (mInitialized) {
368        delete[] mTextMeshPtr;
369        delete[] mTextTexture;
370    }
371
372    if (mTextureId) {
373        glDeleteTextures(1, &mTextureId);
374    }
375
376    Vector<Font*> fontsToDereference = mActiveFonts;
377    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
378        delete fontsToDereference[i];
379    }
380}
381
382void FontRenderer::flushAllAndInvalidate() {
383    if (mCurrentQuadIndex != 0) {
384        issueDrawCommand();
385        mCurrentQuadIndex = 0;
386    }
387    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
388        mActiveFonts[i]->invalidateTextureCache();
389    }
390    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
391        mCacheLines[i]->mCurrentCol = 0;
392    }
393}
394
395bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
396    // If the glyph is too tall, don't cache it
397    if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
398        if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
399            // Default cache not large enough for large glyphs - resize cache to
400            // max size and try again
401            flushAllAndInvalidate();
402            initTextTexture(true);
403        }
404        if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
405            LOGE("Font size to large to fit in cache. width, height = %i, %i",
406                    (int) glyph.fWidth, (int) glyph.fHeight);
407            return false;
408        }
409    }
410
411    // Now copy the bitmap into the cache texture
412    uint32_t startX = 0;
413    uint32_t startY = 0;
414
415    bool bitmapFit = false;
416    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
417        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
418        if (bitmapFit) {
419            break;
420        }
421    }
422
423    // If the new glyph didn't fit, flush the state so far and invalidate everything
424    if (!bitmapFit) {
425        flushAllAndInvalidate();
426
427        // Try to fit it again
428        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
429            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
430            if (bitmapFit) {
431                break;
432            }
433        }
434
435        // if we still don't fit, something is wrong and we shouldn't draw
436        if (!bitmapFit) {
437            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
438                    (int) glyph.fWidth, (int) glyph.fHeight);
439            return false;
440        }
441    }
442
443    *retOriginX = startX;
444    *retOriginY = startY;
445
446    uint32_t endX = startX + glyph.fWidth;
447    uint32_t endY = startY + glyph.fHeight;
448
449    uint32_t cacheWidth = mCacheWidth;
450
451    uint8_t* cacheBuffer = mTextTexture;
452    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
453    unsigned int stride = glyph.rowBytes();
454
455    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
456    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
457        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
458            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
459            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
460        }
461    }
462
463    return true;
464}
465
466void FontRenderer::initTextTexture(bool largeFonts) {
467    mCacheLines.clear();
468    if (largeFonts) {
469        mCacheWidth = MAX_TEXT_CACHE_WIDTH;
470        mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
471    }
472
473    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
474    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
475
476    mUploadTexture = false;
477
478    if (mTextureId != 0) {
479        glDeleteTextures(1, &mTextureId);
480    }
481    glGenTextures(1, &mTextureId);
482    glBindTexture(GL_TEXTURE_2D, mTextureId);
483    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
484    // Initialize texture dimensions
485    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
486            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
487
488    mLinearFiltering = false;
489    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
490    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
491
492    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
493    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
494
495    // Split up our cache texture into lines of certain widths
496    int nextLine = 0;
497    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, 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, 26, 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, 34, nextLine, 0));
506    nextLine += mCacheLines.top()->mMaxHeight;
507    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
508    nextLine += mCacheLines.top()->mMaxHeight;
509    if (largeFonts) {
510        int nextSize = 76;
511        // Make several new lines with increasing font sizes
512        while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
513            mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
514            nextLine += mCacheLines.top()->mMaxHeight;
515            nextSize += 50;
516        }
517    }
518    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
519}
520
521// Avoid having to reallocate memory and render quad by quad
522void FontRenderer::initVertexArrayBuffers() {
523    uint32_t numIndicies = mMaxNumberOfQuads * 6;
524    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
525    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
526
527    // Four verts, two triangles , six indices per quad
528    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
529        int i6 = i * 6;
530        int i4 = i * 4;
531
532        indexBufferData[i6 + 0] = i4 + 0;
533        indexBufferData[i6 + 1] = i4 + 1;
534        indexBufferData[i6 + 2] = i4 + 2;
535
536        indexBufferData[i6 + 3] = i4 + 0;
537        indexBufferData[i6 + 4] = i4 + 2;
538        indexBufferData[i6 + 5] = i4 + 3;
539    }
540
541    glGenBuffers(1, &mIndexBufferID);
542    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
543    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
544    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
545
546    free(indexBufferData);
547
548    uint32_t coordSize = 3;
549    uint32_t uvSize = 2;
550    uint32_t vertsPerQuad = 4;
551    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
552    mTextMeshPtr = new float[vertexBufferSize];
553}
554
555// We don't want to allocate anything unless we actually draw text
556void FontRenderer::checkInit() {
557    if (mInitialized) {
558        return;
559    }
560
561    initTextTexture();
562    initVertexArrayBuffers();
563
564    // We store a string with letters in a rough frequency of occurrence
565    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
566    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
567    mLatinPrecache += String16(",.?!()-+@;:`'");
568    mLatinPrecache += String16("0123456789");
569
570    mInitialized = true;
571}
572
573void FontRenderer::checkTextureUpdate() {
574    if (!mUploadTexture) {
575        return;
576    }
577
578    glBindTexture(GL_TEXTURE_2D, mTextureId);
579
580    // Iterate over all the cache lines and see which ones need to be updated
581    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
582        CacheTextureLine* cl = mCacheLines[i];
583        if(cl->mDirty) {
584            uint32_t xOffset = 0;
585            uint32_t yOffset = cl->mCurrentRow;
586            uint32_t width   = mCacheWidth;
587            uint32_t height  = cl->mMaxHeight;
588            void* textureData = mTextTexture + yOffset*width;
589
590            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
591                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
592
593            cl->mDirty = false;
594        }
595    }
596
597    mUploadTexture = false;
598}
599
600void FontRenderer::issueDrawCommand() {
601    checkTextureUpdate();
602
603    float* vtx = mTextMeshPtr;
604    float* tex = vtx + 3;
605
606    glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
607    glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
608
609    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
610    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
611
612    mDrawn = true;
613}
614
615void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
616        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
617        float x4, float y4, float z4, float u4, float v4) {
618    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
619        return;
620    }
621
622    const uint32_t vertsPerQuad = 4;
623    const uint32_t floatsPerVert = 5;
624    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
625
626    (*currentPos++) = x1;
627    (*currentPos++) = y1;
628    (*currentPos++) = z1;
629    (*currentPos++) = u1;
630    (*currentPos++) = v1;
631
632    (*currentPos++) = x2;
633    (*currentPos++) = y2;
634    (*currentPos++) = z2;
635    (*currentPos++) = u2;
636    (*currentPos++) = v2;
637
638    (*currentPos++) = x3;
639    (*currentPos++) = y3;
640    (*currentPos++) = z3;
641    (*currentPos++) = u3;
642    (*currentPos++) = v3;
643
644    (*currentPos++) = x4;
645    (*currentPos++) = y4;
646    (*currentPos++) = z4;
647    (*currentPos++) = u4;
648    (*currentPos++) = v4;
649
650    mCurrentQuadIndex++;
651
652    if (mBounds) {
653        mBounds->left = fmin(mBounds->left, x1);
654        mBounds->top = fmin(mBounds->top, y3);
655        mBounds->right = fmax(mBounds->right, x3);
656        mBounds->bottom = fmax(mBounds->bottom, y1);
657    }
658
659    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
660        issueDrawCommand();
661        mCurrentQuadIndex = 0;
662    }
663}
664
665uint32_t FontRenderer::getRemainingCacheCapacity() {
666    uint32_t remainingCapacity = 0;
667    float totalPixels = 0;
668    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
669         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
670         totalPixels += mCacheLines[i]->mMaxWidth;
671    }
672    remainingCapacity = (remainingCapacity * 100) / totalPixels;
673    return remainingCapacity;
674}
675
676void FontRenderer::precacheLatin(SkPaint* paint) {
677    // Remaining capacity is measured in %
678    uint32_t remainingCapacity = getRemainingCacheCapacity();
679    uint32_t precacheIdx = 0;
680    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
681        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
682        remainingCapacity = getRemainingCacheCapacity();
683        precacheIdx ++;
684    }
685}
686
687void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
688    uint32_t currentNumFonts = mActiveFonts.size();
689    int flags = 0;
690    if (paint->isFakeBoldText()) {
691        flags |= Font::kFakeBold;
692    }
693
694    const float skewX = paint->getTextSkewX();
695    uint32_t italicStyle = *(uint32_t*) &skewX;
696    const float scaleXFloat = paint->getTextScaleX();
697    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
698    SkPaint::Style style = paint->getStyle();
699    const float strokeWidthFloat = paint->getStrokeWidth();
700    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
701    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
702            scaleX, style, strokeWidth);
703
704    const float maxPrecacheFontSize = 40.0f;
705    bool isNewFont = currentNumFonts != mActiveFonts.size();
706
707    if (isNewFont && fontSize <= maxPrecacheFontSize) {
708        precacheLatin(paint);
709    }
710}
711
712FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
713        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
714    checkInit();
715
716    if (!mCurrentFont) {
717        DropShadow image;
718        image.width = 0;
719        image.height = 0;
720        image.image = NULL;
721        image.penX = 0;
722        image.penY = 0;
723        return image;
724    }
725
726    Rect bounds;
727    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
728    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
729    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
730    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
731    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
732        dataBuffer[i] = 0;
733    }
734
735    int penX = radius - bounds.left;
736    int penY = radius - bounds.bottom;
737
738    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
739            dataBuffer, paddedWidth, paddedHeight);
740    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
741
742    DropShadow image;
743    image.width = paddedWidth;
744    image.height = paddedHeight;
745    image.image = dataBuffer;
746    image.penX = penX;
747    image.penY = penY;
748    return image;
749}
750
751bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
752        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
753    checkInit();
754
755    if (!mCurrentFont) {
756        LOGE("No font set");
757        return false;
758    }
759
760    if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
761        LOGE("Font renderer unable to draw, attribute slots undefined");
762        return false;
763    }
764
765    mDrawn = false;
766    mBounds = bounds;
767    mClip = clip;
768    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
769    mBounds = NULL;
770
771    if (mCurrentQuadIndex != 0) {
772        issueDrawCommand();
773        mCurrentQuadIndex = 0;
774    }
775
776    return mDrawn;
777}
778
779void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
780    // Compute gaussian weights for the blur
781    // e is the euler's number
782    float e = 2.718281828459045f;
783    float pi = 3.1415926535897932f;
784    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
785    // x is of the form [-radius .. 0 .. radius]
786    // and sigma varies with radius.
787    // Based on some experimental radius values and sigma's
788    // we approximately fit sigma = f(radius) as
789    // sigma = radius * 0.3  + 0.6
790    // The larger the radius gets, the more our gaussian blur
791    // will resemble a box blur since with large sigma
792    // the gaussian curve begins to lose its shape
793    float sigma = 0.3f * (float) radius + 0.6f;
794
795    // Now compute the coefficints
796    // We will store some redundant values to save some math during
797    // the blur calculations
798    // precompute some values
799    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
800    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
801
802    float normalizeFactor = 0.0f;
803    for (int32_t r = -radius; r <= radius; r ++) {
804        float floatR = (float) r;
805        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
806        normalizeFactor += weights[r + radius];
807    }
808
809    //Now we need to normalize the weights because all our coefficients need to add up to one
810    normalizeFactor = 1.0f / normalizeFactor;
811    for (int32_t r = -radius; r <= radius; r ++) {
812        weights[r + radius] *= normalizeFactor;
813    }
814}
815
816void FontRenderer::horizontalBlur(float* weights, int32_t radius,
817        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
818    float blurredPixel = 0.0f;
819    float currentPixel = 0.0f;
820
821    for (int32_t y = 0; y < height; y ++) {
822
823        const uint8_t* input = source + y * width;
824        uint8_t* output = dest + y * width;
825
826        for (int32_t x = 0; x < width; x ++) {
827            blurredPixel = 0.0f;
828            const float* gPtr = weights;
829            // Optimization for non-border pixels
830            if (x > radius && x < (width - radius)) {
831                const uint8_t *i = input + (x - radius);
832                for (int r = -radius; r <= radius; r ++) {
833                    currentPixel = (float) (*i);
834                    blurredPixel += currentPixel * gPtr[0];
835                    gPtr++;
836                    i++;
837                }
838            } else {
839                for (int32_t r = -radius; r <= radius; r ++) {
840                    // Stepping left and right away from the pixel
841                    int validW = x + r;
842                    if (validW < 0) {
843                        validW = 0;
844                    }
845                    if (validW > width - 1) {
846                        validW = width - 1;
847                    }
848
849                    currentPixel = (float) input[validW];
850                    blurredPixel += currentPixel * gPtr[0];
851                    gPtr++;
852                }
853            }
854            *output = (uint8_t)blurredPixel;
855            output ++;
856        }
857    }
858}
859
860void FontRenderer::verticalBlur(float* weights, int32_t radius,
861        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
862    float blurredPixel = 0.0f;
863    float currentPixel = 0.0f;
864
865    for (int32_t y = 0; y < height; y ++) {
866
867        uint8_t* output = dest + y * width;
868
869        for (int32_t x = 0; x < width; x ++) {
870            blurredPixel = 0.0f;
871            const float* gPtr = weights;
872            const uint8_t* input = source + x;
873            // Optimization for non-border pixels
874            if (y > radius && y < (height - radius)) {
875                const uint8_t *i = input + ((y - radius) * width);
876                for (int32_t r = -radius; r <= radius; r ++) {
877                    currentPixel = (float)(*i);
878                    blurredPixel += currentPixel * gPtr[0];
879                    gPtr++;
880                    i += width;
881                }
882            } else {
883                for (int32_t r = -radius; r <= radius; r ++) {
884                    int validH = y + r;
885                    // Clamp to zero and width
886                    if (validH < 0) {
887                        validH = 0;
888                    }
889                    if (validH > height - 1) {
890                        validH = height - 1;
891                    }
892
893                    const uint8_t *i = input + validH * width;
894                    currentPixel = (float) (*i);
895                    blurredPixel += currentPixel * gPtr[0];
896                    gPtr++;
897                }
898            }
899            *output = (uint8_t) blurredPixel;
900            output ++;
901        }
902    }
903}
904
905
906void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
907    float *gaussian = new float[2 * radius + 1];
908    computeGaussianWeights(gaussian, radius);
909    uint8_t* scratch = new uint8_t[width * height];
910    horizontalBlur(gaussian, radius, image, scratch, width, height);
911    verticalBlur(gaussian, radius, scratch, image, width, height);
912    delete[] gaussian;
913    delete[] scratch;
914}
915
916}; // namespace uirenderer
917}; // namespace android
918