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