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