FontRenderer.cpp revision 51769a68a5cb34e9564740c6a854fcb93018789d
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
203    mIndexBufferID = 0;
204
205    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
206    mCacheHeight = DEFAULT_TEXT_CACHE_WIDTH;
207
208    char property[PROPERTY_VALUE_MAX];
209    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
210        LOGD("  Setting text cache width to %s pixels", property);
211        mCacheWidth = atoi(property);
212    } else {
213        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
214    }
215
216    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
217        LOGD("  Setting text cache width to %s pixels", property);
218        mCacheHeight = atoi(property);
219    } else {
220        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
221    }
222}
223
224FontRenderer::~FontRenderer() {
225    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
226        delete mCacheLines[i];
227    }
228    mCacheLines.clear();
229
230    delete mTextTexture;
231
232    Vector<Font*> fontsToDereference = mActiveFonts;
233    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
234        delete fontsToDereference[i];
235    }
236}
237
238void FontRenderer::flushAllAndInvalidate() {
239    if (mCurrentQuadIndex != 0) {
240        issueDrawCommand();
241        mCurrentQuadIndex = 0;
242    }
243    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
244        mActiveFonts[i]->invalidateTextureCache();
245    }
246    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
247        mCacheLines[i]->mCurrentCol = 0;
248    }
249}
250
251bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
252    // If the glyph is too tall, don't cache it
253    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
254        LOGE("Font size to large to fit in cache. width, height = %i, %i",
255                (int) glyph.fWidth, (int) glyph.fHeight);
256        return false;
257    }
258
259    // Now copy the bitmap into the cache texture
260    uint32_t startX = 0;
261    uint32_t startY = 0;
262
263    bool bitmapFit = false;
264    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
265        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
266        if (bitmapFit) {
267            break;
268        }
269    }
270
271    // If the new glyph didn't fit, flush the state so far and invalidate everything
272    if (!bitmapFit) {
273        flushAllAndInvalidate();
274
275        // Try to fit it again
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 we still don't fit, something is wrong and we shouldn't draw
284        if (!bitmapFit) {
285            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
286                    (int) glyph.fWidth, (int) glyph.fHeight);
287            return false;
288        }
289    }
290
291    *retOriginX = startX;
292    *retOriginY = startY;
293
294    uint32_t endX = startX + glyph.fWidth;
295    uint32_t endY = startY + glyph.fHeight;
296
297    uint32_t cacheWidth = mCacheWidth;
298
299    unsigned char* cacheBuffer = mTextTexture;
300    unsigned char* bitmapBuffer = (unsigned char*) glyph.fImage;
301    unsigned int stride = glyph.rowBytes();
302
303    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
304    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
305        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
306            unsigned char tempCol = bitmapBuffer[bY * stride + bX];
307            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
308        }
309    }
310
311    return true;
312}
313
314void FontRenderer::initTextTexture() {
315    mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
316    mUploadTexture = false;
317
318    glGenTextures(1, &mTextureId);
319    glBindTexture(GL_TEXTURE_2D, mTextureId);
320    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
321
322    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
323    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
324
325    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
326    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
327
328    // Split up our cache texture into lines of certain widths
329    int nextLine = 0;
330    mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
331    nextLine += mCacheLines.top()->mMaxHeight;
332    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
333    nextLine += mCacheLines.top()->mMaxHeight;
334    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
335    nextLine += mCacheLines.top()->mMaxHeight;
336    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
337    nextLine += mCacheLines.top()->mMaxHeight;
338    mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
339    nextLine += mCacheLines.top()->mMaxHeight;
340    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
341}
342
343// Avoid having to reallocate memory and render quad by quad
344void FontRenderer::initVertexArrayBuffers() {
345    uint32_t numIndicies = mMaxNumberOfQuads * 6;
346    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
347    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
348
349    // Four verts, two triangles , six indices per quad
350    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
351        int i6 = i * 6;
352        int i4 = i * 4;
353
354        indexBufferData[i6 + 0] = i4 + 0;
355        indexBufferData[i6 + 1] = i4 + 1;
356        indexBufferData[i6 + 2] = i4 + 2;
357
358        indexBufferData[i6 + 3] = i4 + 0;
359        indexBufferData[i6 + 4] = i4 + 2;
360        indexBufferData[i6 + 5] = i4 + 3;
361    }
362
363    glGenBuffers(1, &mIndexBufferID);
364    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
365    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
366    glBindBuffer(GL_ARRAY_BUFFER, 0);
367
368    free(indexBufferData);
369
370    uint32_t coordSize = 3;
371    uint32_t uvSize = 2;
372    uint32_t vertsPerQuad = 4;
373    uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize *
374            uvSize * sizeof(float);
375    mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes);
376}
377
378// We don't want to allocate anything unless we actually draw text
379void FontRenderer::checkInit() {
380    if (mInitialized) {
381        return;
382    }
383
384    initTextTexture();
385    initVertexArrayBuffers();
386
387    mInitialized = true;
388}
389
390void FontRenderer::issueDrawCommand() {
391    if (mUploadTexture) {
392        glBindTexture(GL_TEXTURE_2D, mTextureId);
393        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA,
394                GL_UNSIGNED_BYTE, mTextTexture);
395        mUploadTexture = false;
396    }
397
398    float* vtx = mTextMeshPtr;
399    float* tex = vtx + 3;
400
401    // position is slot 0
402    uint32_t slot = 0;
403    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
404
405    // texture0 is slot 1
406    slot = 1;
407    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
408
409    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
410    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
411}
412
413void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
414        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
415        float x4, float y4, float z4, float u4, float v4) {
416    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
417        return;
418    }
419
420    const uint32_t vertsPerQuad = 4;
421    const uint32_t floatsPerVert = 5;
422    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
423
424    (*currentPos++) = x1;
425    (*currentPos++) = y1;
426    (*currentPos++) = z1;
427    (*currentPos++) = u1;
428    (*currentPos++) = v1;
429
430    (*currentPos++) = x2;
431    (*currentPos++) = y2;
432    (*currentPos++) = z2;
433    (*currentPos++) = u2;
434    (*currentPos++) = v2;
435
436    (*currentPos++) = x3;
437    (*currentPos++) = y3;
438    (*currentPos++) = z3;
439    (*currentPos++) = u3;
440    (*currentPos++) = v3;
441
442    (*currentPos++) = x4;
443    (*currentPos++) = y4;
444    (*currentPos++) = z4;
445    (*currentPos++) = u4;
446    (*currentPos++) = v4;
447
448    mCurrentQuadIndex++;
449
450    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
451        issueDrawCommand();
452        mCurrentQuadIndex = 0;
453    }
454}
455
456void FontRenderer::setFont(uint32_t fontId, float fontSize) {
457    mCurrentFont = Font::create(this, fontId, fontSize);
458}
459
460void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
461        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
462    checkInit();
463
464    if (!mCurrentFont) {
465        LOGE("No font set");
466        return;
467    }
468
469    mClip = clip;
470    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
471
472    if (mCurrentQuadIndex != 0) {
473        issueDrawCommand();
474        mCurrentQuadIndex = 0;
475    }
476}
477
478}; // namespace uirenderer
479}; // namespace android
480