FontRenderer.cpp revision 9b9902ddbb01548f4a0199087b7035e7c10b2ae7
137047fceba836f341d0108beed0991b0f8dfc543James Dong/*
237047fceba836f341d0108beed0991b0f8dfc543James Dong * Copyright (C) 2010 The Android Open Source Project
337047fceba836f341d0108beed0991b0f8dfc543James Dong *
437047fceba836f341d0108beed0991b0f8dfc543James Dong * Licensed under the Apache License, Version 2.0 (the "License");
537047fceba836f341d0108beed0991b0f8dfc543James Dong * you may not use this file except in compliance with the License.
637047fceba836f341d0108beed0991b0f8dfc543James Dong * You may obtain a copy of the License at
737047fceba836f341d0108beed0991b0f8dfc543James Dong *
837047fceba836f341d0108beed0991b0f8dfc543James Dong *      http://www.apache.org/licenses/LICENSE-2.0
937047fceba836f341d0108beed0991b0f8dfc543James Dong *
1037047fceba836f341d0108beed0991b0f8dfc543James Dong * Unless required by applicable law or agreed to in writing, software
1137047fceba836f341d0108beed0991b0f8dfc543James Dong * distributed under the License is distributed on an "AS IS" BASIS,
1237047fceba836f341d0108beed0991b0f8dfc543James Dong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1337047fceba836f341d0108beed0991b0f8dfc543James Dong * See the License for the specific language governing permissions and
1437047fceba836f341d0108beed0991b0f8dfc543James Dong * limitations under the License.
1537047fceba836f341d0108beed0991b0f8dfc543James Dong */
1637047fceba836f341d0108beed0991b0f8dfc543James Dong
1737047fceba836f341d0108beed0991b0f8dfc543James Dong#define LOG_TAG "OpenGLRenderer"
1837047fceba836f341d0108beed0991b0f8dfc543James Dong
1937047fceba836f341d0108beed0991b0f8dfc543James Dong#include <SkUtils.h>
2037047fceba836f341d0108beed0991b0f8dfc543James Dong
2137047fceba836f341d0108beed0991b0f8dfc543James Dong#include <cutils/properties.h>
2237047fceba836f341d0108beed0991b0f8dfc543James Dong#include <utils/Log.h>
2337047fceba836f341d0108beed0991b0f8dfc543James Dong
2437047fceba836f341d0108beed0991b0f8dfc543James Dong#include "FontRenderer.h"
2537047fceba836f341d0108beed0991b0f8dfc543James Dong
2637047fceba836f341d0108beed0991b0f8dfc543James Dongnamespace android {
2737047fceba836f341d0108beed0991b0f8dfc543James Dongnamespace uirenderer {
2837047fceba836f341d0108beed0991b0f8dfc543James Dong
2937047fceba836f341d0108beed0991b0f8dfc543James Dong///////////////////////////////////////////////////////////////////////////////
3037047fceba836f341d0108beed0991b0f8dfc543James Dong// Defines
3137047fceba836f341d0108beed0991b0f8dfc543James Dong///////////////////////////////////////////////////////////////////////////////
3237047fceba836f341d0108beed0991b0f8dfc543James Dong
3337047fceba836f341d0108beed0991b0f8dfc543James Dong#define DEFAULT_TEXT_CACHE_WIDTH 1024
3437047fceba836f341d0108beed0991b0f8dfc543James Dong#define DEFAULT_TEXT_CACHE_HEIGHT 256
3537047fceba836f341d0108beed0991b0f8dfc543James Dong
3637047fceba836f341d0108beed0991b0f8dfc543James Dong///////////////////////////////////////////////////////////////////////////////
3737047fceba836f341d0108beed0991b0f8dfc543James Dong// Font
3837047fceba836f341d0108beed0991b0f8dfc543James Dong///////////////////////////////////////////////////////////////////////////////
3937047fceba836f341d0108beed0991b0f8dfc543James Dong
4037047fceba836f341d0108beed0991b0f8dfc543James DongFont::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
4137047fceba836f341d0108beed0991b0f8dfc543James Dong    mState(state), mFontId(fontId), mFontSize(fontSize) {
4237047fceba836f341d0108beed0991b0f8dfc543James Dong}
4337047fceba836f341d0108beed0991b0f8dfc543James Dong
4437047fceba836f341d0108beed0991b0f8dfc543James Dong
4537047fceba836f341d0108beed0991b0f8dfc543James DongFont::~Font() {
4637047fceba836f341d0108beed0991b0f8dfc543James Dong    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
4737047fceba836f341d0108beed0991b0f8dfc543James Dong        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