FontRenderer.cpp revision 694b519ac647fe998fd396fe0784cc8e179aadc4
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 "FontRenderer.h"
20
21#include <SkUtils.h>
22
23namespace android {
24namespace uirenderer {
25
26///////////////////////////////////////////////////////////////////////////////
27// Font
28///////////////////////////////////////////////////////////////////////////////
29
30Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
31    mState(state), mFontId(fontId), mFontSize(fontSize) {
32}
33
34
35Font::~Font() {
36    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
37        if (mState->mActiveFonts[ct] == this) {
38            mState->mActiveFonts.removeAt(ct);
39            break;
40        }
41    }
42
43    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
44        CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
45        delete glyph;
46    }
47}
48
49void Font::invalidateTextureCache() {
50    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
51        mCachedGlyphs.valueAt(i)->mIsValid = false;
52    }
53}
54
55void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
56    FontRenderer *state = mState;
57
58    int nPenX = x + glyph->mBitmapLeft;
59    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
60
61    state->appendMeshQuad(nPenX, nPenY, 0, glyph->mBitmapMinU, glyph->mBitmapMaxV,
62            nPenX + (int) glyph->mBitmapWidth, nPenY, 0, glyph->mBitmapMaxU, glyph->mBitmapMaxV,
63            nPenX + (int) glyph->mBitmapWidth, nPenY - (int) glyph->mBitmapHeight,
64            0, glyph->mBitmapMaxU, glyph->mBitmapMinV, nPenX, nPenY - (int) glyph->mBitmapHeight,
65            0, glyph->mBitmapMinU, glyph->mBitmapMinV);
66}
67
68void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, int numGlyphs,
69        int x, int y) {
70    if (numGlyphs == 0 || text == NULL || len == 0) {
71        return;
72    }
73
74    int penX = x, penY = y;
75    int glyphsLeft = 1;
76    if (numGlyphs > 0) {
77        glyphsLeft = numGlyphs;
78    }
79
80    //size_t index = start;
81    //size_t nextIndex = 0;
82
83    text += start;
84
85    while (glyphsLeft > 0) {
86        //int32_t utfChar = utf32_at(text, len, index, &nextIndex);
87        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
88
89        // Reached the end of the string or encountered
90        if (utfChar < 0) {
91            break;
92        }
93
94        // Move to the next character in the array
95        //index = nextIndex;
96
97        CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor(utfChar);
98
99        if (cachedGlyph == NULL) {
100            cachedGlyph = cacheGlyph(paint, utfChar);
101        }
102        // Is the glyph still in texture cache?
103        if (!cachedGlyph->mIsValid) {
104            const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
105            updateGlyphCache(paint, skiaGlyph, cachedGlyph);
106        }
107
108        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
109        if (cachedGlyph->mIsValid) {
110            drawCachedGlyph(cachedGlyph, penX, penY);
111        }
112
113        // TODO: Check how to do this conversion
114        penX += SkFixedRound(cachedGlyph->mAdvanceX);
115
116        // If we were given a specific number of glyphs, decrement
117        if (numGlyphs > 0) {
118            glyphsLeft--;
119        }
120    }
121}
122
123void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph) {
124    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
125    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
126    glyph->mBitmapLeft = skiaGlyph.fLeft;
127    glyph->mBitmapTop = skiaGlyph.fTop;
128
129    uint32_t startX = 0;
130    uint32_t startY = 0;
131
132    // Let the font state figure out where to put the bitmap
133    FontRenderer *state = mState;
134    // Get the bitmap for the glyph
135    paint->findImage(skiaGlyph);
136    glyph->mIsValid = state->cacheBitmap(skiaGlyph, &startX, &startY);
137
138    if (!glyph->mIsValid) {
139        return;
140    }
141
142    uint32_t endX = startX + skiaGlyph.fWidth;
143    uint32_t endY = startY + skiaGlyph.fHeight;
144
145    glyph->mBitmapWidth = skiaGlyph.fWidth;
146    glyph->mBitmapHeight = skiaGlyph.fHeight;
147
148    uint32_t cacheWidth = state->getCacheWidth();
149    uint32_t cacheHeight = state->getCacheHeight();
150
151    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
152    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
153    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
154    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
155
156    state->mUploadTexture = true;
157}
158
159Font::CachedGlyphInfo *Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
160    CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
161    mCachedGlyphs.add(glyph, newGlyph);
162
163    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
164    newGlyph->mGlyphIndex = skiaGlyph.fID;
165    newGlyph->mIsValid = false;
166
167    updateGlyphCache(paint, skiaGlyph, newGlyph);
168
169    return newGlyph;
170}
171
172Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
173    Vector<Font*> &activeFonts = state->mActiveFonts;
174
175    for (uint32_t i = 0; i < activeFonts.size(); i++) {
176        Font *ithFont = activeFonts[i];
177        if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) {
178            return ithFont;
179        }
180    }
181
182    Font* newFont = new Font(state, fontId, fontSize);
183    activeFonts.push(newFont);
184    return newFont;
185}
186
187///////////////////////////////////////////////////////////////////////////////
188// FontRenderer
189///////////////////////////////////////////////////////////////////////////////
190
191FontRenderer::FontRenderer() {
192    mInitialized = false;
193    mMaxNumberOfQuads = 1024;
194    mCurrentQuadIndex = 0;
195
196    mIndexBufferID = 0;
197
198    mCacheWidth = 1024;
199    mCacheHeight = 256;
200}
201
202FontRenderer::~FontRenderer() {
203    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
204        delete mCacheLines[i];
205    }
206    mCacheLines.clear();
207
208    delete mTextTexture;
209
210    Vector<Font*> fontsToDereference = mActiveFonts;
211    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
212        delete fontsToDereference[i];
213    }
214}
215
216void FontRenderer::flushAllAndInvalidate() {
217    if (mCurrentQuadIndex != 0) {
218        issueDrawCommand();
219        mCurrentQuadIndex = 0;
220    }
221    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
222        mActiveFonts[i]->invalidateTextureCache();
223    }
224    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
225        mCacheLines[i]->mCurrentCol = 0;
226    }
227}
228
229bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
230    // If the glyph is too tall, don't cache it
231    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
232        LOGE("Font size to large to fit in cache. width, height = %i, %i",
233                (int) glyph.fWidth, (int) glyph.fHeight);
234        return false;
235    }
236
237    // Now copy the bitmap into the cache texture
238    uint32_t startX = 0;
239    uint32_t startY = 0;
240
241    bool bitmapFit = false;
242    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
243        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
244        if (bitmapFit) {
245            break;
246        }
247    }
248
249    // If the new glyph didn't fit, flush the state so far and invalidate everything
250    if (!bitmapFit) {
251        flushAllAndInvalidate();
252
253        // Try to fit it again
254        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
255            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
256            if (bitmapFit) {
257                break;
258            }
259        }
260
261        // if we still don't fit, something is wrong and we shouldn't draw
262        if (!bitmapFit) {
263            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
264                    (int) glyph.fWidth, (int) glyph.fHeight);
265            return false;
266        }
267    }
268
269    *retOriginX = startX;
270    *retOriginY = startY;
271
272    uint32_t endX = startX + glyph.fWidth;
273    uint32_t endY = startY + glyph.fHeight;
274
275    uint32_t cacheWidth = mCacheWidth;
276
277    unsigned char *cacheBuffer = mTextTexture;
278    unsigned char *bitmapBuffer = (unsigned char*) glyph.fImage;
279    unsigned int stride = glyph.rowBytes();
280
281    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
282    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
283        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
284            unsigned char tempCol = bitmapBuffer[bY * stride + bX];
285            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
286        }
287    }
288
289    return true;
290}
291
292void FontRenderer::initTextTexture() {
293    mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
294    mUploadTexture = false;
295
296    glGenTextures(1, &mTextureId);
297    glBindTexture(GL_TEXTURE_2D, mTextureId);
298    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
299
300    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
301    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
302
303    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
304    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
305
306    // Split up our cache texture into lines of certain widths
307    int nextLine = 0;
308    mCacheLines.push(new CacheTextureLine(16, mCacheWidth, nextLine, 0));
309    nextLine += mCacheLines.top()->mMaxHeight;
310    mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0));
311    nextLine += mCacheLines.top()->mMaxHeight;
312    mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
313    nextLine += mCacheLines.top()->mMaxHeight;
314    mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
315    nextLine += mCacheLines.top()->mMaxHeight;
316    mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0));
317    nextLine += mCacheLines.top()->mMaxHeight;
318    mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, nextLine, 0));
319}
320
321// Avoid having to reallocate memory and render quad by quad
322void FontRenderer::initVertexArrayBuffers() {
323    uint32_t numIndicies = mMaxNumberOfQuads * 6;
324    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
325    uint16_t *indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
326
327    // Four verts, two triangles , six indices per quad
328    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
329        int i6 = i * 6;
330        int i4 = i * 4;
331
332        indexBufferData[i6 + 0] = i4 + 0;
333        indexBufferData[i6 + 1] = i4 + 1;
334        indexBufferData[i6 + 2] = i4 + 2;
335
336        indexBufferData[i6 + 3] = i4 + 0;
337        indexBufferData[i6 + 4] = i4 + 2;
338        indexBufferData[i6 + 5] = i4 + 3;
339    }
340
341    glGenBuffers(1, &mIndexBufferID);
342    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
343    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
344    glBindBuffer(GL_ARRAY_BUFFER, 0);
345
346    free(indexBufferData);
347
348    uint32_t coordSize = 3;
349    uint32_t uvSize = 2;
350    uint32_t vertsPerQuad = 4;
351    uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize *
352            uvSize * sizeof(float);
353    mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes);
354}
355
356// We don't want to allocate anything unless we actually draw text
357void FontRenderer::checkInit() {
358    if (mInitialized) {
359        return;
360    }
361
362    initTextTexture();
363    initVertexArrayBuffers();
364
365    mInitialized = true;
366}
367
368void FontRenderer::issueDrawCommand() {
369    if (mUploadTexture) {
370        glBindTexture(GL_TEXTURE_2D, mTextureId);
371        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA,
372                GL_UNSIGNED_BYTE, mTextTexture);
373        mUploadTexture = false;
374    }
375
376    float *vtx = mTextMeshPtr;
377    float *tex = vtx + 3;
378
379    // position is slot 0
380    uint32_t slot = 0;
381    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
382
383    // texture0 is slot 1
384    slot = 1;
385    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
386
387    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
388    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
389}
390
391void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
392        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
393        float x4, float y4, float z4, float u4, float v4) {
394    const uint32_t vertsPerQuad = 4;
395    const uint32_t floatsPerVert = 5;
396    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
397
398    // TODO: Cull things that are off the screen
399    //    float width = (float)mRSC->getWidth();
400    //    float height = (float)mRSC->getHeight();
401    //
402    //    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
403    //        return;
404    //    }
405
406    (*currentPos++) = x1;
407    (*currentPos++) = y1;
408    (*currentPos++) = z1;
409    (*currentPos++) = u1;
410    (*currentPos++) = v1;
411
412    (*currentPos++) = x2;
413    (*currentPos++) = y2;
414    (*currentPos++) = z2;
415    (*currentPos++) = u2;
416    (*currentPos++) = v2;
417
418    (*currentPos++) = x3;
419    (*currentPos++) = y3;
420    (*currentPos++) = z3;
421    (*currentPos++) = u3;
422    (*currentPos++) = v3;
423
424    (*currentPos++) = x4;
425    (*currentPos++) = y4;
426    (*currentPos++) = z4;
427    (*currentPos++) = u4;
428    (*currentPos++) = v4;
429
430    mCurrentQuadIndex++;
431
432    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
433        issueDrawCommand();
434        mCurrentQuadIndex = 0;
435    }
436}
437
438void FontRenderer::setFont(uint32_t fontId, float fontSize) {
439    mCurrentFont = Font::create(this, fontId, fontSize);
440}
441
442void FontRenderer::renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex,
443        int numGlyphs, int x, int y) {
444    checkInit();
445
446    // Render code here
447    Font *currentFont = mCurrentFont;
448    if (!currentFont) {
449        LOGE("Unable to initialize any fonts");
450        return;
451    }
452
453    currentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y);
454
455    if (mCurrentQuadIndex != 0) {
456        issueDrawCommand();
457        mCurrentQuadIndex = 0;
458    }
459}
460
461void FontRenderer::renderText(SkPaint* paint, const char *text, int x, int y) {
462    size_t textLen = strlen(text);
463    renderText(paint, text, textLen, 0, -1, x, y);
464}
465
466}; // namespace uirenderer
467}; // namespace android
468