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