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