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