FontRenderer.cpp revision 65ef909776c03417d8b597738da54ca211e37e4f
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#include <utils/Log.h>
23
24#include "FontRenderer.h"
25
26namespace android {
27namespace uirenderer {
28
29///////////////////////////////////////////////////////////////////////////////
30// Defines
31///////////////////////////////////////////////////////////////////////////////
32
33#define DEFAULT_TEXT_CACHE_WIDTH 1024
34#define DEFAULT_TEXT_CACHE_HEIGHT 256
35
36///////////////////////////////////////////////////////////////////////////////
37// Font
38///////////////////////////////////////////////////////////////////////////////
39
40Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
41    mState(state), mFontId(fontId), mFontSize(fontSize) {
42}
43
44
45Font::~Font() {
46    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
47        if (mState->mActiveFonts[ct] == this) {
48            mState->mActiveFonts.removeAt(ct);
49            break;
50        }
51    }
52
53    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
54        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
55        delete glyph;
56    }
57}
58
59void Font::invalidateTextureCache() {
60    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
61        mCachedGlyphs.valueAt(i)->mIsValid = false;
62    }
63}
64
65void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
66    int nPenX = x + glyph->mBitmapLeft;
67    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
68
69    float u1 = glyph->mBitmapMinU;
70    float u2 = glyph->mBitmapMaxU;
71    float v1 = glyph->mBitmapMinV;
72    float v2 = glyph->mBitmapMaxV;
73
74    int width = (int) glyph->mBitmapWidth;
75    int height = (int) glyph->mBitmapHeight;
76
77    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
78            nPenX + width, nPenY, 0, u2, v2,
79            nPenX + width, nPenY - height, 0, u2, v1,
80            nPenX, nPenY - height, 0, u1, v1);
81}
82
83Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
84    CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar);
85    if (cachedGlyph == NULL) {
86        cachedGlyph = cacheGlyph(paint, utfChar);
87    }
88
89    // Is the glyph still in texture cache?
90    if (!cachedGlyph->mIsValid) {
91        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
92        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
93    }
94
95    return cachedGlyph;
96}
97
98void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
99        int numGlyphs, int x, int y) {
100    if (numGlyphs == 0 || text == NULL || len == 0) {
101        return;
102    }
103
104    int penX = x, penY = y;
105    int glyphsLeft = 1;
106    if (numGlyphs > 0) {
107        glyphsLeft = numGlyphs;
108    }
109
110    text += start;
111
112    while (glyphsLeft > 0) {
113        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
114
115        // Reached the end of the string or encountered
116        if (utfChar < 0) {
117            break;
118        }
119
120        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
121
122        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
123        if (cachedGlyph->mIsValid) {
124            drawCachedGlyph(cachedGlyph, penX, penY);
125        }
126
127        penX += SkFixedFloor(cachedGlyph->mAdvanceX);
128
129        // If we were given a specific number of glyphs, decrement
130        if (numGlyphs > 0) {
131            glyphsLeft--;
132        }
133    }
134}
135
136void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
137    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
138    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
139    glyph->mBitmapLeft = skiaGlyph.fLeft;
140    glyph->mBitmapTop = skiaGlyph.fTop;
141
142    uint32_t startX = 0;
143    uint32_t startY = 0;
144
145    // Get the bitmap for the glyph
146    paint->findImage(skiaGlyph);
147    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
148
149    if (!glyph->mIsValid) {
150        return;
151    }
152
153    uint32_t endX = startX + skiaGlyph.fWidth;
154    uint32_t endY = startY + skiaGlyph.fHeight;
155
156    glyph->mBitmapWidth = skiaGlyph.fWidth;
157    glyph->mBitmapHeight = skiaGlyph.fHeight;
158
159    uint32_t cacheWidth = mState->getCacheWidth();
160    uint32_t cacheHeight = mState->getCacheHeight();
161
162    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
163    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
164    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
165    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
166
167    mState->mUploadTexture = true;
168}
169
170Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
171    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
172    mCachedGlyphs.add(glyph, newGlyph);
173
174    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
175    newGlyph->mGlyphIndex = skiaGlyph.fID;
176    newGlyph->mIsValid = false;
177
178    updateGlyphCache(paint, skiaGlyph, newGlyph);
179
180    return newGlyph;
181}
182
183Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
184    Vector<Font*> &activeFonts = state->mActiveFonts;
185
186    for (uint32_t i = 0; i < activeFonts.size(); i++) {
187        Font* font = activeFonts[i];
188        if (font->mFontId == fontId && font->mFontSize == fontSize) {
189            return font;
190        }
191    }
192
193    Font* newFont = new Font(state, fontId, fontSize);
194    activeFonts.push(newFont);
195    return newFont;
196}
197
198///////////////////////////////////////////////////////////////////////////////
199// FontRenderer
200///////////////////////////////////////////////////////////////////////////////
201
202FontRenderer::FontRenderer() {
203    LOGD("Creating FontRenderer");
204
205    mInitialized = false;
206    mMaxNumberOfQuads = 1024;
207    mCurrentQuadIndex = 0;
208    mTextureId = 0;
209
210    mIndexBufferID = 0;
211
212    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
213    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
214
215    char property[PROPERTY_VALUE_MAX];
216    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
217        LOGD("  Setting text cache width to %s pixels", property);
218        mCacheWidth = atoi(property);
219    } else {
220        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
221    }
222
223    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
224        LOGD("  Setting text cache width to %s pixels", property);
225        mCacheHeight = atoi(property);
226    } else {
227        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
228    }
229}
230
231FontRenderer::~FontRenderer() {
232    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
233        delete mCacheLines[i];
234    }
235    mCacheLines.clear();
236
237    delete mTextMeshPtr;
238
239    delete mTextTexture;
240    if(mTextureId) {
241        glDeleteTextures(1, &mTextureId);
242    }
243
244    Vector<Font*> fontsToDereference = mActiveFonts;
245    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
246        delete fontsToDereference[i];
247    }
248}
249
250void FontRenderer::flushAllAndInvalidate() {
251    if (mCurrentQuadIndex != 0) {
252        issueDrawCommand();
253        mCurrentQuadIndex = 0;
254    }
255    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
256        mActiveFonts[i]->invalidateTextureCache();
257    }
258    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
259        mCacheLines[i]->mCurrentCol = 0;
260    }
261}
262
263bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
264    // If the glyph is too tall, don't cache it
265    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
266        LOGE("Font size to large to fit in cache. width, height = %i, %i",
267                (int) glyph.fWidth, (int) glyph.fHeight);
268        return false;
269    }
270
271    // Now copy the bitmap into the cache texture
272    uint32_t startX = 0;
273    uint32_t startY = 0;
274
275    bool bitmapFit = false;
276    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
277        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
278        if (bitmapFit) {
279            break;
280        }
281    }
282
283    // If the new glyph didn't fit, flush the state so far and invalidate everything
284    if (!bitmapFit) {
285        flushAllAndInvalidate();
286
287        // Try to fit it again
288        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
289            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
290            if (bitmapFit) {
291                break;
292            }
293        }
294
295        // if we still don't fit, something is wrong and we shouldn't draw
296        if (!bitmapFit) {
297            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
298                    (int) glyph.fWidth, (int) glyph.fHeight);
299            return false;
300        }
301    }
302
303    *retOriginX = startX;
304    *retOriginY = startY;
305
306    uint32_t endX = startX + glyph.fWidth;
307    uint32_t endY = startY + glyph.fHeight;
308
309    uint32_t cacheWidth = mCacheWidth;
310
311    unsigned char* cacheBuffer = mTextTexture;
312    unsigned char* bitmapBuffer = (unsigned char*) glyph.fImage;
313    unsigned int stride = glyph.rowBytes();
314
315    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
316    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
317        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
318            unsigned char tempCol = bitmapBuffer[bY * stride + bX];
319            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
320        }
321    }
322
323    return true;
324}
325
326void FontRenderer::initTextTexture() {
327    mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
328    mUploadTexture = false;
329
330    glGenTextures(1, &mTextureId);
331    glBindTexture(GL_TEXTURE_2D, mTextureId);
332    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
333    // Initialize texture dimentions
334    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
335                  GL_ALPHA, GL_UNSIGNED_BYTE, 0);
336
337    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
338    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
339
340    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
341    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
342
343    // Split up our cache texture into lines of certain widths
344    int nextLine = 0;
345    mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
346    nextLine += mCacheLines.top()->mMaxHeight;
347    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
348    nextLine += mCacheLines.top()->mMaxHeight;
349    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
350    nextLine += mCacheLines.top()->mMaxHeight;
351    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
352    nextLine += mCacheLines.top()->mMaxHeight;
353    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
354    nextLine += mCacheLines.top()->mMaxHeight;
355    mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
356    nextLine += mCacheLines.top()->mMaxHeight;
357    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
358}
359
360// Avoid having to reallocate memory and render quad by quad
361void FontRenderer::initVertexArrayBuffers() {
362    uint32_t numIndicies = mMaxNumberOfQuads * 6;
363    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
364    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
365
366    // Four verts, two triangles , six indices per quad
367    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
368        int i6 = i * 6;
369        int i4 = i * 4;
370
371        indexBufferData[i6 + 0] = i4 + 0;
372        indexBufferData[i6 + 1] = i4 + 1;
373        indexBufferData[i6 + 2] = i4 + 2;
374
375        indexBufferData[i6 + 3] = i4 + 0;
376        indexBufferData[i6 + 4] = i4 + 2;
377        indexBufferData[i6 + 5] = i4 + 3;
378    }
379
380    glGenBuffers(1, &mIndexBufferID);
381    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
382    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
383    glBindBuffer(GL_ARRAY_BUFFER, 0);
384
385    free(indexBufferData);
386
387    uint32_t coordSize = 3;
388    uint32_t uvSize = 2;
389    uint32_t vertsPerQuad = 4;
390    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
391    mTextMeshPtr = new float[vertexBufferSize];
392}
393
394// We don't want to allocate anything unless we actually draw text
395void FontRenderer::checkInit() {
396    if (mInitialized) {
397        return;
398    }
399
400    initTextTexture();
401    initVertexArrayBuffers();
402
403    // We store a string with letters in a rough frequency of occurrence
404    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
405    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
406    mLatinPrecache += String16(",.?!()-+@;:`'");
407    mLatinPrecache += String16("0123456789");
408
409    mInitialized = true;
410}
411
412void FontRenderer::checkTextureUpdate() {
413    if (!mUploadTexture) {
414        return;
415    }
416
417    glBindTexture(GL_TEXTURE_2D, mTextureId);
418
419    // Iterate over all the cache lines and see which ones need to be updated
420    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
421        CacheTextureLine* cl = mCacheLines[i];
422        if(cl->mDirty) {
423            uint32_t xOffset = 0;
424            uint32_t yOffset = cl->mCurrentRow;
425            uint32_t width   = mCacheWidth;
426            uint32_t height  = cl->mMaxHeight;
427            void*    textureData = mTextTexture + yOffset*width;
428
429            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
430                             GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
431
432            cl->mDirty = false;
433        }
434    }
435
436    mUploadTexture = false;
437}
438
439void FontRenderer::issueDrawCommand() {
440
441    checkTextureUpdate();
442
443    float* vtx = mTextMeshPtr;
444    float* tex = vtx + 3;
445
446    // position is slot 0
447    uint32_t slot = 0;
448    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
449
450    // texture0 is slot 1
451    slot = 1;
452    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
453
454    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
455    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
456}
457
458void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
459        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
460        float x4, float y4, float z4, float u4, float v4) {
461    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
462        return;
463    }
464
465    const uint32_t vertsPerQuad = 4;
466    const uint32_t floatsPerVert = 5;
467    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
468
469    (*currentPos++) = x1;
470    (*currentPos++) = y1;
471    (*currentPos++) = z1;
472    (*currentPos++) = u1;
473    (*currentPos++) = v1;
474
475    (*currentPos++) = x2;
476    (*currentPos++) = y2;
477    (*currentPos++) = z2;
478    (*currentPos++) = u2;
479    (*currentPos++) = v2;
480
481    (*currentPos++) = x3;
482    (*currentPos++) = y3;
483    (*currentPos++) = z3;
484    (*currentPos++) = u3;
485    (*currentPos++) = v3;
486
487    (*currentPos++) = x4;
488    (*currentPos++) = y4;
489    (*currentPos++) = z4;
490    (*currentPos++) = u4;
491    (*currentPos++) = v4;
492
493    mCurrentQuadIndex++;
494
495    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
496        issueDrawCommand();
497        mCurrentQuadIndex = 0;
498    }
499}
500
501uint32_t FontRenderer::getRemainingCacheCapacity() {
502    uint32_t remainingCapacity = 0;
503    float totalPixels = 0;
504    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
505         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
506         totalPixels += mCacheLines[i]->mMaxWidth;
507    }
508    remainingCapacity = (remainingCapacity * 100) / totalPixels;
509    return remainingCapacity;
510}
511
512void FontRenderer::precacheLatin(SkPaint* paint) {
513    // Remaining capacity is measured in %
514    uint32_t remainingCapacity = getRemainingCacheCapacity();
515    uint32_t precacheIdx = 0;
516    while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
517        mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]);
518        remainingCapacity = getRemainingCacheCapacity();
519        precacheIdx ++;
520    }
521}
522
523void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
524    uint32_t currentNumFonts = mActiveFonts.size();
525    mCurrentFont = Font::create(this, fontId, fontSize);
526
527    const float maxPrecacheFontSize = 40.0f;
528    bool isNewFont = currentNumFonts != mActiveFonts.size();
529
530    if(isNewFont && fontSize <= maxPrecacheFontSize ){
531        precacheLatin(paint);
532    }
533}
534
535void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
536        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
537    checkInit();
538
539    if (!mCurrentFont) {
540        LOGE("No font set");
541        return;
542    }
543
544    mClip = clip;
545    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
546
547    if (mCurrentQuadIndex != 0) {
548        issueDrawCommand();
549        mCurrentQuadIndex = 0;
550    }
551}
552
553}; // namespace uirenderer
554}; // namespace android
555