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