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