FontRenderer.cpp revision 8668f8a633d9299091556c3b2e5ae07be8dce360
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, uint32_t scaleX) :
44        mState(state), mFontId(fontId), mFontSize(fontSize),
45        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
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, uint32_t scaleX) {
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                font->mScaleX == scaleX) {
290            return font;
291        }
292    }
293
294    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
295    activeFonts.push(newFont);
296    return newFont;
297}
298
299///////////////////////////////////////////////////////////////////////////////
300// FontRenderer
301///////////////////////////////////////////////////////////////////////////////
302
303static bool sLogFontRendererCreate = true;
304
305FontRenderer::FontRenderer() {
306    if (sLogFontRendererCreate) {
307        INIT_LOGD("Creating FontRenderer");
308    }
309
310    mGammaTable = NULL;
311    mInitialized = false;
312    mMaxNumberOfQuads = 1024;
313    mCurrentQuadIndex = 0;
314    mTextureId = 0;
315
316    mTextMeshPtr = NULL;
317    mTextTexture = NULL;
318
319    mIndexBufferID = 0;
320    mPositionAttrSlot = -1;
321    mTexcoordAttrSlot = -1;
322
323    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
324    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
325
326    char property[PROPERTY_VALUE_MAX];
327    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
328        if (sLogFontRendererCreate) {
329            INIT_LOGD("  Setting text cache width to %s pixels", property);
330        }
331        mCacheWidth = atoi(property);
332    } else {
333        if (sLogFontRendererCreate) {
334            INIT_LOGD("  Using default text cache width of %i pixels", mCacheWidth);
335        }
336    }
337
338    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
339        if (sLogFontRendererCreate) {
340            INIT_LOGD("  Setting text cache width to %s pixels", property);
341        }
342        mCacheHeight = atoi(property);
343    } else {
344        if (sLogFontRendererCreate) {
345            INIT_LOGD("  Using default text cache height of %i pixels", mCacheHeight);
346        }
347    }
348
349    sLogFontRendererCreate = false;
350}
351
352FontRenderer::~FontRenderer() {
353    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
354        delete mCacheLines[i];
355    }
356    mCacheLines.clear();
357
358    if (mInitialized) {
359        delete[] mTextMeshPtr;
360        delete[] mTextTexture;
361    }
362
363    if (mTextureId) {
364        glDeleteTextures(1, &mTextureId);
365    }
366
367    Vector<Font*> fontsToDereference = mActiveFonts;
368    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
369        delete fontsToDereference[i];
370    }
371}
372
373void FontRenderer::flushAllAndInvalidate() {
374    if (mCurrentQuadIndex != 0) {
375        issueDrawCommand();
376        mCurrentQuadIndex = 0;
377    }
378    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
379        mActiveFonts[i]->invalidateTextureCache();
380    }
381    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
382        mCacheLines[i]->mCurrentCol = 0;
383    }
384}
385
386bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
387    // If the glyph is too tall, don't cache it
388    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
389        LOGE("Font size to large to fit in cache. width, height = %i, %i",
390                (int) glyph.fWidth, (int) glyph.fHeight);
391        return false;
392    }
393
394    // Now copy the bitmap into the cache texture
395    uint32_t startX = 0;
396    uint32_t startY = 0;
397
398    bool bitmapFit = false;
399    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
400        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
401        if (bitmapFit) {
402            break;
403        }
404    }
405
406    // If the new glyph didn't fit, flush the state so far and invalidate everything
407    if (!bitmapFit) {
408        flushAllAndInvalidate();
409
410        // Try to fit it again
411        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
412            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
413            if (bitmapFit) {
414                break;
415            }
416        }
417
418        // if we still don't fit, something is wrong and we shouldn't draw
419        if (!bitmapFit) {
420            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
421                    (int) glyph.fWidth, (int) glyph.fHeight);
422            return false;
423        }
424    }
425
426    *retOriginX = startX;
427    *retOriginY = startY;
428
429    uint32_t endX = startX + glyph.fWidth;
430    uint32_t endY = startY + glyph.fHeight;
431
432    uint32_t cacheWidth = mCacheWidth;
433
434    uint8_t* cacheBuffer = mTextTexture;
435    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
436    unsigned int stride = glyph.rowBytes();
437
438    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
439    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
440        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
441            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
442            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
443        }
444    }
445
446    return true;
447}
448
449void FontRenderer::initTextTexture() {
450    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
451    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
452
453    mUploadTexture = false;
454
455    glGenTextures(1, &mTextureId);
456    glBindTexture(GL_TEXTURE_2D, mTextureId);
457    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
458    // Initialize texture dimentions
459    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
460            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
461
462    mLinearFiltering = false;
463    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
464    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
465
466    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
467    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
468
469    // Split up our cache texture into lines of certain widths
470    int nextLine = 0;
471    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
472    nextLine += mCacheLines.top()->mMaxHeight;
473    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
474    nextLine += mCacheLines.top()->mMaxHeight;
475    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
476    nextLine += mCacheLines.top()->mMaxHeight;
477    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
478    nextLine += mCacheLines.top()->mMaxHeight;
479    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
480    nextLine += mCacheLines.top()->mMaxHeight;
481    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
482    nextLine += mCacheLines.top()->mMaxHeight;
483    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
484}
485
486// Avoid having to reallocate memory and render quad by quad
487void FontRenderer::initVertexArrayBuffers() {
488    uint32_t numIndicies = mMaxNumberOfQuads * 6;
489    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
490    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
491
492    // Four verts, two triangles , six indices per quad
493    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
494        int i6 = i * 6;
495        int i4 = i * 4;
496
497        indexBufferData[i6 + 0] = i4 + 0;
498        indexBufferData[i6 + 1] = i4 + 1;
499        indexBufferData[i6 + 2] = i4 + 2;
500
501        indexBufferData[i6 + 3] = i4 + 0;
502        indexBufferData[i6 + 4] = i4 + 2;
503        indexBufferData[i6 + 5] = i4 + 3;
504    }
505
506    glGenBuffers(1, &mIndexBufferID);
507    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
508    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
509    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
510
511    free(indexBufferData);
512
513    uint32_t coordSize = 3;
514    uint32_t uvSize = 2;
515    uint32_t vertsPerQuad = 4;
516    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
517    mTextMeshPtr = new float[vertexBufferSize];
518}
519
520// We don't want to allocate anything unless we actually draw text
521void FontRenderer::checkInit() {
522    if (mInitialized) {
523        return;
524    }
525
526    initTextTexture();
527    initVertexArrayBuffers();
528
529    // We store a string with letters in a rough frequency of occurrence
530    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
531    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
532    mLatinPrecache += String16(",.?!()-+@;:`'");
533    mLatinPrecache += String16("0123456789");
534
535    mInitialized = true;
536}
537
538void FontRenderer::checkTextureUpdate() {
539    if (!mUploadTexture) {
540        return;
541    }
542
543    glBindTexture(GL_TEXTURE_2D, mTextureId);
544
545    // Iterate over all the cache lines and see which ones need to be updated
546    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
547        CacheTextureLine* cl = mCacheLines[i];
548        if(cl->mDirty) {
549            uint32_t xOffset = 0;
550            uint32_t yOffset = cl->mCurrentRow;
551            uint32_t width   = mCacheWidth;
552            uint32_t height  = cl->mMaxHeight;
553            void* textureData = mTextTexture + yOffset*width;
554
555            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
556                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
557
558            cl->mDirty = false;
559        }
560    }
561
562    mUploadTexture = false;
563}
564
565void FontRenderer::issueDrawCommand() {
566    checkTextureUpdate();
567
568    float* vtx = mTextMeshPtr;
569    float* tex = vtx + 3;
570
571    glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
572    glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
573
574    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
575    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
576
577    mDrawn = true;
578}
579
580void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
581        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
582        float x4, float y4, float z4, float u4, float v4) {
583    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
584        return;
585    }
586
587    const uint32_t vertsPerQuad = 4;
588    const uint32_t floatsPerVert = 5;
589    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
590
591    (*currentPos++) = x1;
592    (*currentPos++) = y1;
593    (*currentPos++) = z1;
594    (*currentPos++) = u1;
595    (*currentPos++) = v1;
596
597    (*currentPos++) = x2;
598    (*currentPos++) = y2;
599    (*currentPos++) = z2;
600    (*currentPos++) = u2;
601    (*currentPos++) = v2;
602
603    (*currentPos++) = x3;
604    (*currentPos++) = y3;
605    (*currentPos++) = z3;
606    (*currentPos++) = u3;
607    (*currentPos++) = v3;
608
609    (*currentPos++) = x4;
610    (*currentPos++) = y4;
611    (*currentPos++) = z4;
612    (*currentPos++) = u4;
613    (*currentPos++) = v4;
614
615    mCurrentQuadIndex++;
616
617    if (mBounds) {
618        mBounds->left = fmin(mBounds->left, x1);
619        mBounds->top = fmin(mBounds->top, y3);
620        mBounds->right = fmax(mBounds->right, x3);
621        mBounds->bottom = fmax(mBounds->bottom, y1);
622    }
623
624    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
625        issueDrawCommand();
626        mCurrentQuadIndex = 0;
627    }
628}
629
630uint32_t FontRenderer::getRemainingCacheCapacity() {
631    uint32_t remainingCapacity = 0;
632    float totalPixels = 0;
633    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
634         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
635         totalPixels += mCacheLines[i]->mMaxWidth;
636    }
637    remainingCapacity = (remainingCapacity * 100) / totalPixels;
638    return remainingCapacity;
639}
640
641void FontRenderer::precacheLatin(SkPaint* paint) {
642    // Remaining capacity is measured in %
643    uint32_t remainingCapacity = getRemainingCacheCapacity();
644    uint32_t precacheIdx = 0;
645    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
646        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
647        remainingCapacity = getRemainingCacheCapacity();
648        precacheIdx ++;
649    }
650}
651
652void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
653    uint32_t currentNumFonts = mActiveFonts.size();
654    int flags = 0;
655    if (paint->isFakeBoldText()) {
656        flags |= Font::kFakeBold;
657    }
658
659    const float skewX = paint->getTextSkewX();
660    uint32_t italicStyle = *(uint32_t*) &skewX;
661    const float scaleXFloat = paint->getTextScaleX();
662    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
663    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
664
665    const float maxPrecacheFontSize = 40.0f;
666    bool isNewFont = currentNumFonts != mActiveFonts.size();
667
668    if (isNewFont && fontSize <= maxPrecacheFontSize) {
669        precacheLatin(paint);
670    }
671}
672
673FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
674        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
675    checkInit();
676
677    if (!mCurrentFont) {
678        DropShadow image;
679        image.width = 0;
680        image.height = 0;
681        image.image = NULL;
682        image.penX = 0;
683        image.penY = 0;
684        return image;
685    }
686
687    Rect bounds;
688    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
689    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
690    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
691    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
692    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
693        dataBuffer[i] = 0;
694    }
695
696    int penX = radius - bounds.left;
697    int penY = radius - bounds.bottom;
698
699    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
700            dataBuffer, paddedWidth, paddedHeight);
701    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
702
703    DropShadow image;
704    image.width = paddedWidth;
705    image.height = paddedHeight;
706    image.image = dataBuffer;
707    image.penX = penX;
708    image.penY = penY;
709    return image;
710}
711
712bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
713        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
714    checkInit();
715
716    if (!mCurrentFont) {
717        LOGE("No font set");
718        return false;
719    }
720
721    if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
722        LOGE("Font renderer unable to draw, attribute slots undefined");
723        return false;
724    }
725
726    mDrawn = false;
727    mBounds = bounds;
728    mClip = clip;
729    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
730    mBounds = NULL;
731
732    if (mCurrentQuadIndex != 0) {
733        issueDrawCommand();
734        mCurrentQuadIndex = 0;
735    }
736
737    return mDrawn;
738}
739
740void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
741    // Compute gaussian weights for the blur
742    // e is the euler's number
743    float e = 2.718281828459045f;
744    float pi = 3.1415926535897932f;
745    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
746    // x is of the form [-radius .. 0 .. radius]
747    // and sigma varies with radius.
748    // Based on some experimental radius values and sigma's
749    // we approximately fit sigma = f(radius) as
750    // sigma = radius * 0.3  + 0.6
751    // The larger the radius gets, the more our gaussian blur
752    // will resemble a box blur since with large sigma
753    // the gaussian curve begins to lose its shape
754    float sigma = 0.3f * (float) radius + 0.6f;
755
756    // Now compute the coefficints
757    // We will store some redundant values to save some math during
758    // the blur calculations
759    // precompute some values
760    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
761    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
762
763    float normalizeFactor = 0.0f;
764    for (int32_t r = -radius; r <= radius; r ++) {
765        float floatR = (float) r;
766        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
767        normalizeFactor += weights[r + radius];
768    }
769
770    //Now we need to normalize the weights because all our coefficients need to add up to one
771    normalizeFactor = 1.0f / normalizeFactor;
772    for (int32_t r = -radius; r <= radius; r ++) {
773        weights[r + radius] *= normalizeFactor;
774    }
775}
776
777void FontRenderer::horizontalBlur(float* weights, int32_t radius,
778        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
779    float blurredPixel = 0.0f;
780    float currentPixel = 0.0f;
781
782    for (int32_t y = 0; y < height; y ++) {
783
784        const uint8_t* input = source + y * width;
785        uint8_t* output = dest + y * width;
786
787        for (int32_t x = 0; x < width; x ++) {
788            blurredPixel = 0.0f;
789            const float* gPtr = weights;
790            // Optimization for non-border pixels
791            if (x > radius && x < (width - radius)) {
792                const uint8_t *i = input + (x - radius);
793                for (int r = -radius; r <= radius; r ++) {
794                    currentPixel = (float) (*i);
795                    blurredPixel += currentPixel * gPtr[0];
796                    gPtr++;
797                    i++;
798                }
799            } else {
800                for (int32_t r = -radius; r <= radius; r ++) {
801                    // Stepping left and right away from the pixel
802                    int validW = x + r;
803                    if (validW < 0) {
804                        validW = 0;
805                    }
806                    if (validW > width - 1) {
807                        validW = width - 1;
808                    }
809
810                    currentPixel = (float) input[validW];
811                    blurredPixel += currentPixel * gPtr[0];
812                    gPtr++;
813                }
814            }
815            *output = (uint8_t)blurredPixel;
816            output ++;
817        }
818    }
819}
820
821void FontRenderer::verticalBlur(float* weights, int32_t radius,
822        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
823    float blurredPixel = 0.0f;
824    float currentPixel = 0.0f;
825
826    for (int32_t y = 0; y < height; y ++) {
827
828        uint8_t* output = dest + y * width;
829
830        for (int32_t x = 0; x < width; x ++) {
831            blurredPixel = 0.0f;
832            const float* gPtr = weights;
833            const uint8_t* input = source + x;
834            // Optimization for non-border pixels
835            if (y > radius && y < (height - radius)) {
836                const uint8_t *i = input + ((y - radius) * width);
837                for (int32_t r = -radius; r <= radius; r ++) {
838                    currentPixel = (float)(*i);
839                    blurredPixel += currentPixel * gPtr[0];
840                    gPtr++;
841                    i += width;
842                }
843            } else {
844                for (int32_t r = -radius; r <= radius; r ++) {
845                    int validH = y + r;
846                    // Clamp to zero and width
847                    if (validH < 0) {
848                        validH = 0;
849                    }
850                    if (validH > height - 1) {
851                        validH = height - 1;
852                    }
853
854                    const uint8_t *i = input + validH * width;
855                    currentPixel = (float) (*i);
856                    blurredPixel += currentPixel * gPtr[0];
857                    gPtr++;
858                }
859            }
860            *output = (uint8_t) blurredPixel;
861            output ++;
862        }
863    }
864}
865
866
867void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
868    float *gaussian = new float[2 * radius + 1];
869    computeGaussianWeights(gaussian, radius);
870    uint8_t* scratch = new uint8_t[width * height];
871    horizontalBlur(gaussian, radius, image, scratch, width, height);
872    verticalBlur(gaussian, radius, scratch, image, width, height);
873    delete[] gaussian;
874    delete[] scratch;
875}
876
877}; // namespace uirenderer
878}; // namespace android
879