FontRenderer.cpp revision 2577db1ec135a1470a2c42139772ec97a6c30e78
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
23#include <utils/Log.h>
24
25#include "FontRenderer.h"
26
27namespace android {
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
31// Defines
32///////////////////////////////////////////////////////////////////////////////
33
34#define DEFAULT_TEXT_CACHE_WIDTH 1024
35#define DEFAULT_TEXT_CACHE_HEIGHT 256
36
37///////////////////////////////////////////////////////////////////////////////
38// Font
39///////////////////////////////////////////////////////////////////////////////
40
41Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
42        int flags, uint32_t italicStyle) :
43        mState(state), mFontId(fontId), mFontSize(fontSize),
44        mFlags(flags), mItalicStyle(italicStyle) {
45}
46
47
48Font::~Font() {
49    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
50        if (mState->mActiveFonts[ct] == this) {
51            mState->mActiveFonts.removeAt(ct);
52            break;
53        }
54    }
55
56    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
57        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
58        delete glyph;
59    }
60}
61
62void Font::invalidateTextureCache() {
63    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
64        mCachedGlyphs.valueAt(i)->mIsValid = false;
65    }
66}
67
68void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
69    int nPenX = x + glyph->mBitmapLeft;
70    int nPenY = y + glyph->mBitmapTop;
71
72    int width = (int) glyph->mBitmapWidth;
73    int height = (int) glyph->mBitmapHeight;
74
75    if (bounds->bottom > nPenY) {
76        bounds->bottom = nPenY;
77    }
78    if (bounds->left > nPenX) {
79        bounds->left = nPenX;
80    }
81    if (bounds->right < nPenX + width) {
82        bounds->right = nPenX + width;
83    }
84    if (bounds->top < nPenY + height) {
85        bounds->top = nPenY + height;
86    }
87}
88
89void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
90    int nPenX = x + glyph->mBitmapLeft;
91    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
92
93    float u1 = glyph->mBitmapMinU;
94    float u2 = glyph->mBitmapMaxU;
95    float v1 = glyph->mBitmapMinV;
96    float v2 = glyph->mBitmapMaxV;
97
98    int width = (int) glyph->mBitmapWidth;
99    int height = (int) glyph->mBitmapHeight;
100
101    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
102            nPenX + width, nPenY, 0, u2, v2,
103            nPenX + width, nPenY - height, 0, u2, v1,
104            nPenX, nPenY - height, 0, u1, v1);
105}
106
107void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
108        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
109    int nPenX = x + glyph->mBitmapLeft;
110    int nPenY = y + glyph->mBitmapTop;
111
112    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
113    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
114
115    uint32_t cacheWidth = mState->getCacheWidth();
116    const uint8_t* cacheBuffer = mState->getTextTextureData();
117
118    uint32_t cacheX = 0, cacheY = 0;
119    int32_t bX = 0, bY = 0;
120    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
121        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
122            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
123                LOGE("Skipping invalid index");
124                continue;
125            }
126            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
127            bitmap[bY * bitmapW + bX] = tempCol;
128        }
129    }
130
131}
132
133Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
134    CachedGlyphInfo* cachedGlyph = NULL;
135    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
136    if (index >= 0) {
137        cachedGlyph = mCachedGlyphs.valueAt(index);
138    } else {
139        cachedGlyph = cacheGlyph(paint, utfChar);
140    }
141
142    // Is the glyph still in texture cache?
143    if (!cachedGlyph->mIsValid) {
144        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
145        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
146    }
147
148    return cachedGlyph;
149}
150
151void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
152        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
153    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
154        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
155                bitmapW, bitmapH, NULL);
156    } else {
157        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
158    }
159
160}
161
162void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
163        int numGlyphs, Rect *bounds) {
164    if (bounds == NULL) {
165        LOGE("No return rectangle provided to measure text");
166        return;
167    }
168    bounds->set(1e6, -1e6, -1e6, 1e6);
169    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
170}
171
172#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
173
174void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
175        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
176        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
177    if (numGlyphs == 0 || text == NULL || len == 0) {
178        return;
179    }
180
181    SkFixed penX = SkIntToFixed(x);
182    int penY = y;
183    int glyphsLeft = 1;
184    if (numGlyphs > 0) {
185        glyphsLeft = numGlyphs;
186    }
187
188    SkFixed prevRsbDelta = 0;
189    penX += SK_Fixed1 / 2;
190
191    text += start;
192
193    while (glyphsLeft > 0) {
194        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
195
196        // Reached the end of the string
197        if (utfChar < 0) {
198            break;
199        }
200
201        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
202        penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
203        prevRsbDelta = cachedGlyph->mRsbDelta;
204
205        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
206        if (cachedGlyph->mIsValid) {
207            switch(mode) {
208            case FRAMEBUFFER:
209                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
210                break;
211            case BITMAP:
212                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
213                break;
214            case MEASURE:
215                measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
216                break;
217            }
218        }
219
220        penX += cachedGlyph->mAdvanceX;
221
222        // If we were given a specific number of glyphs, decrement
223        if (numGlyphs > 0) {
224            glyphsLeft--;
225        }
226    }
227}
228
229void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
230    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
231    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
232    glyph->mBitmapLeft = skiaGlyph.fLeft;
233    glyph->mBitmapTop = skiaGlyph.fTop;
234    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
235    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
236
237    uint32_t startX = 0;
238    uint32_t startY = 0;
239
240    // Get the bitmap for the glyph
241    paint->findImage(skiaGlyph);
242    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
243
244    if (!glyph->mIsValid) {
245        return;
246    }
247
248    uint32_t endX = startX + skiaGlyph.fWidth;
249    uint32_t endY = startY + skiaGlyph.fHeight;
250
251    glyph->mStartX = startX;
252    glyph->mStartY = startY;
253    glyph->mBitmapWidth = skiaGlyph.fWidth;
254    glyph->mBitmapHeight = skiaGlyph.fHeight;
255
256    uint32_t cacheWidth = mState->getCacheWidth();
257    uint32_t cacheHeight = mState->getCacheHeight();
258
259    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
260    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
261    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
262    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
263
264    mState->mUploadTexture = true;
265}
266
267Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
268    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
269    mCachedGlyphs.add(glyph, newGlyph);
270
271    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
272    newGlyph->mGlyphIndex = skiaGlyph.fID;
273    newGlyph->mIsValid = false;
274
275    updateGlyphCache(paint, skiaGlyph, newGlyph);
276
277    return newGlyph;
278}
279
280Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
281        int flags, uint32_t italicStyle) {
282    Vector<Font*> &activeFonts = state->mActiveFonts;
283
284    for (uint32_t i = 0; i < activeFonts.size(); i++) {
285        Font* font = activeFonts[i];
286        if (font->mFontId == fontId && font->mFontSize == fontSize &&
287                font->mFlags == flags && font->mItalicStyle == italicStyle) {
288            return font;
289        }
290    }
291
292    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
293    activeFonts.push(newFont);
294    return newFont;
295}
296
297///////////////////////////////////////////////////////////////////////////////
298// FontRenderer
299///////////////////////////////////////////////////////////////////////////////
300
301FontRenderer::FontRenderer() {
302    LOGD("Creating FontRenderer");
303
304    mGammaTable = NULL;
305    mInitialized = false;
306    mMaxNumberOfQuads = 1024;
307    mCurrentQuadIndex = 0;
308    mTextureId = 0;
309
310    mTextMeshPtr = NULL;
311    mTextTexture = NULL;
312
313    mIndexBufferID = 0;
314
315    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
316    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
317
318    char property[PROPERTY_VALUE_MAX];
319    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
320        LOGD("  Setting text cache width to %s pixels", property);
321        mCacheWidth = atoi(property);
322    } else {
323        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
324    }
325
326    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
327        LOGD("  Setting text cache width to %s pixels", property);
328        mCacheHeight = atoi(property);
329    } else {
330        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
331    }
332}
333
334FontRenderer::~FontRenderer() {
335    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
336        delete mCacheLines[i];
337    }
338    mCacheLines.clear();
339
340    if (mInitialized) {
341        delete[] mTextMeshPtr;
342        delete[] mTextTexture;
343    }
344
345    if (mTextureId) {
346        glDeleteTextures(1, &mTextureId);
347    }
348
349    Vector<Font*> fontsToDereference = mActiveFonts;
350    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
351        delete fontsToDereference[i];
352    }
353}
354
355void FontRenderer::flushAllAndInvalidate() {
356    if (mCurrentQuadIndex != 0) {
357        issueDrawCommand();
358        mCurrentQuadIndex = 0;
359    }
360    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
361        mActiveFonts[i]->invalidateTextureCache();
362    }
363    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
364        mCacheLines[i]->mCurrentCol = 0;
365    }
366}
367
368bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
369    // If the glyph is too tall, don't cache it
370    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
371        LOGE("Font size to large to fit in cache. width, height = %i, %i",
372                (int) glyph.fWidth, (int) glyph.fHeight);
373        return false;
374    }
375
376    // Now copy the bitmap into the cache texture
377    uint32_t startX = 0;
378    uint32_t startY = 0;
379
380    bool bitmapFit = false;
381    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
382        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
383        if (bitmapFit) {
384            break;
385        }
386    }
387
388    // If the new glyph didn't fit, flush the state so far and invalidate everything
389    if (!bitmapFit) {
390        flushAllAndInvalidate();
391
392        // Try to fit it again
393        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
394            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
395            if (bitmapFit) {
396                break;
397            }
398        }
399
400        // if we still don't fit, something is wrong and we shouldn't draw
401        if (!bitmapFit) {
402            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
403                    (int) glyph.fWidth, (int) glyph.fHeight);
404            return false;
405        }
406    }
407
408    *retOriginX = startX;
409    *retOriginY = startY;
410
411    uint32_t endX = startX + glyph.fWidth;
412    uint32_t endY = startY + glyph.fHeight;
413
414    uint32_t cacheWidth = mCacheWidth;
415
416    uint8_t* cacheBuffer = mTextTexture;
417    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
418    unsigned int stride = glyph.rowBytes();
419
420    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
421    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
422        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
423            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
424            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
425        }
426    }
427
428    return true;
429}
430
431void FontRenderer::initTextTexture() {
432    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
433    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
434
435    mUploadTexture = false;
436
437    glGenTextures(1, &mTextureId);
438    glBindTexture(GL_TEXTURE_2D, mTextureId);
439    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
440    // Initialize texture dimentions
441    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
442            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
443
444    mLinearFiltering = false;
445    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
446    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
447
448    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
449    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
450
451    // Split up our cache texture into lines of certain widths
452    int nextLine = 0;
453    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
454    nextLine += mCacheLines.top()->mMaxHeight;
455    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
456    nextLine += mCacheLines.top()->mMaxHeight;
457    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
458    nextLine += mCacheLines.top()->mMaxHeight;
459    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
460    nextLine += mCacheLines.top()->mMaxHeight;
461    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
462    nextLine += mCacheLines.top()->mMaxHeight;
463    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
464    nextLine += mCacheLines.top()->mMaxHeight;
465    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
466}
467
468// Avoid having to reallocate memory and render quad by quad
469void FontRenderer::initVertexArrayBuffers() {
470    uint32_t numIndicies = mMaxNumberOfQuads * 6;
471    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
472    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
473
474    // Four verts, two triangles , six indices per quad
475    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
476        int i6 = i * 6;
477        int i4 = i * 4;
478
479        indexBufferData[i6 + 0] = i4 + 0;
480        indexBufferData[i6 + 1] = i4 + 1;
481        indexBufferData[i6 + 2] = i4 + 2;
482
483        indexBufferData[i6 + 3] = i4 + 0;
484        indexBufferData[i6 + 4] = i4 + 2;
485        indexBufferData[i6 + 5] = i4 + 3;
486    }
487
488    glGenBuffers(1, &mIndexBufferID);
489    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
490    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
491    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
492
493    free(indexBufferData);
494
495    uint32_t coordSize = 3;
496    uint32_t uvSize = 2;
497    uint32_t vertsPerQuad = 4;
498    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
499    mTextMeshPtr = new float[vertexBufferSize];
500}
501
502// We don't want to allocate anything unless we actually draw text
503void FontRenderer::checkInit() {
504    if (mInitialized) {
505        return;
506    }
507
508    initTextTexture();
509    initVertexArrayBuffers();
510
511    // We store a string with letters in a rough frequency of occurrence
512    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
513    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
514    mLatinPrecache += String16(",.?!()-+@;:`'");
515    mLatinPrecache += String16("0123456789");
516
517    mInitialized = true;
518}
519
520void FontRenderer::checkTextureUpdate() {
521    if (!mUploadTexture) {
522        return;
523    }
524
525    glBindTexture(GL_TEXTURE_2D, mTextureId);
526
527    // Iterate over all the cache lines and see which ones need to be updated
528    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
529        CacheTextureLine* cl = mCacheLines[i];
530        if(cl->mDirty) {
531            uint32_t xOffset = 0;
532            uint32_t yOffset = cl->mCurrentRow;
533            uint32_t width   = mCacheWidth;
534            uint32_t height  = cl->mMaxHeight;
535            void* textureData = mTextTexture + yOffset*width;
536
537            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
538                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
539
540            cl->mDirty = false;
541        }
542    }
543
544    mUploadTexture = false;
545}
546
547void FontRenderer::issueDrawCommand() {
548    checkTextureUpdate();
549
550    float* vtx = mTextMeshPtr;
551    float* tex = vtx + 3;
552
553    // position is slot 0
554    uint32_t slot = 0;
555    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
556
557    // texture0 is slot 1
558    slot = 1;
559    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
560
561    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
562    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
563
564    mDrawn = true;
565}
566
567void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
568        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
569        float x4, float y4, float z4, float u4, float v4) {
570    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
571        return;
572    }
573
574    const uint32_t vertsPerQuad = 4;
575    const uint32_t floatsPerVert = 5;
576    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
577
578    (*currentPos++) = x1;
579    (*currentPos++) = y1;
580    (*currentPos++) = z1;
581    (*currentPos++) = u1;
582    (*currentPos++) = v1;
583
584    (*currentPos++) = x2;
585    (*currentPos++) = y2;
586    (*currentPos++) = z2;
587    (*currentPos++) = u2;
588    (*currentPos++) = v2;
589
590    (*currentPos++) = x3;
591    (*currentPos++) = y3;
592    (*currentPos++) = z3;
593    (*currentPos++) = u3;
594    (*currentPos++) = v3;
595
596    (*currentPos++) = x4;
597    (*currentPos++) = y4;
598    (*currentPos++) = z4;
599    (*currentPos++) = u4;
600    (*currentPos++) = v4;
601
602    mCurrentQuadIndex++;
603
604    if (mBounds) {
605        mBounds->left = fmin(mBounds->left, x1);
606        mBounds->top = fmin(mBounds->top, y3);
607        mBounds->right = fmax(mBounds->right, x3);
608        mBounds->bottom = fmax(mBounds->bottom, y1);
609    }
610
611    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
612        issueDrawCommand();
613        mCurrentQuadIndex = 0;
614    }
615}
616
617uint32_t FontRenderer::getRemainingCacheCapacity() {
618    uint32_t remainingCapacity = 0;
619    float totalPixels = 0;
620    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
621         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
622         totalPixels += mCacheLines[i]->mMaxWidth;
623    }
624    remainingCapacity = (remainingCapacity * 100) / totalPixels;
625    return remainingCapacity;
626}
627
628void FontRenderer::precacheLatin(SkPaint* paint) {
629    // Remaining capacity is measured in %
630    uint32_t remainingCapacity = getRemainingCacheCapacity();
631    uint32_t precacheIdx = 0;
632    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
633        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
634        remainingCapacity = getRemainingCacheCapacity();
635        precacheIdx ++;
636    }
637}
638
639void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
640    uint32_t currentNumFonts = mActiveFonts.size();
641    int flags = 0;
642    if (paint->isFakeBoldText()) {
643        flags |= Font::kFakeBold;
644    }
645
646    const float skewX = paint->getTextSkewX();
647    uint32_t italicStyle = *(uint32_t*) &skewX;
648    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
649
650    const float maxPrecacheFontSize = 40.0f;
651    bool isNewFont = currentNumFonts != mActiveFonts.size();
652
653    if (isNewFont && fontSize <= maxPrecacheFontSize) {
654        precacheLatin(paint);
655    }
656}
657
658FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
659        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
660    checkInit();
661
662    if (!mCurrentFont) {
663        DropShadow image;
664        image.width = 0;
665        image.height = 0;
666        image.image = NULL;
667        image.penX = 0;
668        image.penY = 0;
669        return image;
670    }
671
672    Rect bounds;
673    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
674    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
675    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
676    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
677    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
678        dataBuffer[i] = 0;
679    }
680
681    int penX = radius - bounds.left;
682    int penY = radius - bounds.bottom;
683
684    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
685            dataBuffer, paddedWidth, paddedHeight);
686    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
687
688    DropShadow image;
689    image.width = paddedWidth;
690    image.height = paddedHeight;
691    image.image = dataBuffer;
692    image.penX = penX;
693    image.penY = penY;
694    return image;
695}
696
697bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
698        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
699    checkInit();
700
701    if (!mCurrentFont) {
702        LOGE("No font set");
703        return false;
704    }
705
706    mDrawn = false;
707    mBounds = bounds;
708    mClip = clip;
709    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
710    mBounds = NULL;
711
712    if (mCurrentQuadIndex != 0) {
713        issueDrawCommand();
714        mCurrentQuadIndex = 0;
715    }
716
717    return mDrawn;
718}
719
720void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
721    // Compute gaussian weights for the blur
722    // e is the euler's number
723    float e = 2.718281828459045f;
724    float pi = 3.1415926535897932f;
725    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
726    // x is of the form [-radius .. 0 .. radius]
727    // and sigma varies with radius.
728    // Based on some experimental radius values and sigma's
729    // we approximately fit sigma = f(radius) as
730    // sigma = radius * 0.3  + 0.6
731    // The larger the radius gets, the more our gaussian blur
732    // will resemble a box blur since with large sigma
733    // the gaussian curve begins to lose its shape
734    float sigma = 0.3f * (float) radius + 0.6f;
735
736    // Now compute the coefficints
737    // We will store some redundant values to save some math during
738    // the blur calculations
739    // precompute some values
740    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
741    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
742
743    float normalizeFactor = 0.0f;
744    for (int32_t r = -radius; r <= radius; r ++) {
745        float floatR = (float) r;
746        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
747        normalizeFactor += weights[r + radius];
748    }
749
750    //Now we need to normalize the weights because all our coefficients need to add up to one
751    normalizeFactor = 1.0f / normalizeFactor;
752    for (int32_t r = -radius; r <= radius; r ++) {
753        weights[r + radius] *= normalizeFactor;
754    }
755}
756
757void FontRenderer::horizontalBlur(float* weights, int32_t radius,
758        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
759    float blurredPixel = 0.0f;
760    float currentPixel = 0.0f;
761
762    for (int32_t y = 0; y < height; y ++) {
763
764        const uint8_t* input = source + y * width;
765        uint8_t* output = dest + y * width;
766
767        for (int32_t x = 0; x < width; x ++) {
768            blurredPixel = 0.0f;
769            const float* gPtr = weights;
770            // Optimization for non-border pixels
771            if (x > radius && x < (width - radius)) {
772                const uint8_t *i = input + (x - radius);
773                for (int r = -radius; r <= radius; r ++) {
774                    currentPixel = (float) (*i);
775                    blurredPixel += currentPixel * gPtr[0];
776                    gPtr++;
777                    i++;
778                }
779            } else {
780                for (int32_t r = -radius; r <= radius; r ++) {
781                    // Stepping left and right away from the pixel
782                    int validW = x + r;
783                    if (validW < 0) {
784                        validW = 0;
785                    }
786                    if (validW > width - 1) {
787                        validW = width - 1;
788                    }
789
790                    currentPixel = (float) input[validW];
791                    blurredPixel += currentPixel * gPtr[0];
792                    gPtr++;
793                }
794            }
795            *output = (uint8_t)blurredPixel;
796            output ++;
797        }
798    }
799}
800
801void FontRenderer::verticalBlur(float* weights, int32_t radius,
802        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
803    float blurredPixel = 0.0f;
804    float currentPixel = 0.0f;
805
806    for (int32_t y = 0; y < height; y ++) {
807
808        uint8_t* output = dest + y * width;
809
810        for (int32_t x = 0; x < width; x ++) {
811            blurredPixel = 0.0f;
812            const float* gPtr = weights;
813            const uint8_t* input = source + x;
814            // Optimization for non-border pixels
815            if (y > radius && y < (height - radius)) {
816                const uint8_t *i = input + ((y - radius) * width);
817                for (int32_t r = -radius; r <= radius; r ++) {
818                    currentPixel = (float)(*i);
819                    blurredPixel += currentPixel * gPtr[0];
820                    gPtr++;
821                    i += width;
822                }
823            } else {
824                for (int32_t r = -radius; r <= radius; r ++) {
825                    int validH = y + r;
826                    // Clamp to zero and width
827                    if (validH < 0) {
828                        validH = 0;
829                    }
830                    if (validH > height - 1) {
831                        validH = height - 1;
832                    }
833
834                    const uint8_t *i = input + validH * width;
835                    currentPixel = (float) (*i);
836                    blurredPixel += currentPixel * gPtr[0];
837                    gPtr++;
838                }
839            }
840            *output = (uint8_t) blurredPixel;
841            output ++;
842        }
843    }
844}
845
846
847void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
848    float *gaussian = new float[2 * radius + 1];
849    computeGaussianWeights(gaussian, radius);
850    uint8_t* scratch = new uint8_t[width * height];
851    horizontalBlur(gaussian, radius, image, scratch, width, height);
852    verticalBlur(gaussian, radius, scratch, image, width, height);
853    delete[] gaussian;
854    delete[] scratch;
855}
856
857}; // namespace uirenderer
858}; // namespace android
859