FontRenderer.cpp revision 15641a6181cdaac2aadf07585de66483b4b7ae6b
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 "RenderScript.h"
26
27#include "utils/Timing.h"
28#include "Caches.h"
29#include "Debug.h"
30#include "FontRenderer.h"
31#include "Rect.h"
32
33namespace android {
34namespace uirenderer {
35
36// blur inputs smaller than this constant will bypass renderscript
37#define RS_MIN_INPUT_CUTOFF 10000
38
39///////////////////////////////////////////////////////////////////////////////
40// FontRenderer
41///////////////////////////////////////////////////////////////////////////////
42
43static bool sLogFontRendererCreate = true;
44
45FontRenderer::FontRenderer() :
46        mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
47
48    if (sLogFontRendererCreate) {
49        INIT_LOGD("Creating FontRenderer");
50    }
51
52    mGammaTable = NULL;
53    mInitialized = false;
54    mMaxNumberOfQuads = 1024;
55    mCurrentQuadIndex = 0;
56    mLastQuadIndex = 0;
57
58    mTextMesh = NULL;
59    mCurrentCacheTexture = NULL;
60
61    mLinearFiltering = false;
62
63    mIndexBufferID = 0;
64
65    mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
66    mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
67    mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
68    mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
69
70    char property[PROPERTY_VALUE_MAX];
71    if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
72        mSmallCacheWidth = atoi(property);
73    }
74
75    if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
76        mSmallCacheHeight = atoi(property);
77    }
78
79    if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
80        mLargeCacheWidth = atoi(property);
81    }
82
83    if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
84        mLargeCacheHeight = atoi(property);
85    }
86
87    uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
88    mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
89    mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
90    mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
91    mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
92
93    if (sLogFontRendererCreate) {
94        INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
95                mSmallCacheWidth, mSmallCacheHeight,
96                mLargeCacheWidth, mLargeCacheHeight >> 1,
97                mLargeCacheWidth, mLargeCacheHeight >> 1,
98                mLargeCacheWidth, mLargeCacheHeight);
99    }
100
101    sLogFontRendererCreate = false;
102}
103
104FontRenderer::~FontRenderer() {
105    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
106        delete mCacheTextures[i];
107    }
108    mCacheTextures.clear();
109
110    if (mInitialized) {
111        // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
112        Caches::getInstance().unbindIndicesBuffer();
113        glDeleteBuffers(1, &mIndexBufferID);
114
115        delete[] mTextMesh;
116    }
117
118    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
119    while (it.next()) {
120        delete it.value();
121    }
122    mActiveFonts.clear();
123}
124
125void FontRenderer::flushAllAndInvalidate() {
126    if (mCurrentQuadIndex != 0) {
127        issueDrawCommand();
128    }
129
130    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
131    while (it.next()) {
132        it.value()->invalidateTextureCache();
133    }
134
135    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
136        mCacheTextures[i]->init();
137    }
138
139#if DEBUG_FONT_RENDERER
140    uint16_t totalGlyphs = 0;
141    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
142        totalGlyphs += mCacheTextures[i]->getGlyphCount();
143        // Erase caches, just as a debugging facility
144        if (mCacheTextures[i]->getTexture()) {
145            memset(mCacheTextures[i]->getTexture(), 0,
146                    mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
147        }
148    }
149    ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
150#endif
151}
152
153void FontRenderer::flushLargeCaches() {
154    // Start from 1; don't deallocate smallest/default texture
155    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
156        CacheTexture* cacheTexture = mCacheTextures[i];
157        if (cacheTexture->getTexture()) {
158            cacheTexture->init();
159            LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
160            while (it.next()) {
161                it.value()->invalidateTextureCache(cacheTexture);
162            }
163            cacheTexture->releaseTexture();
164        }
165    }
166}
167
168CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
169        uint32_t* startX, uint32_t* startY) {
170    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
171        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
172            return mCacheTextures[i];
173        }
174    }
175    // Could not fit glyph into current cache textures
176    return NULL;
177}
178
179void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
180        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
181    checkInit();
182    cachedGlyph->mIsValid = false;
183    // If the glyph is too tall, don't cache it
184    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
185                mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
186        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
187                (int) glyph.fWidth, (int) glyph.fHeight);
188        return;
189    }
190
191    // Now copy the bitmap into the cache texture
192    uint32_t startX = 0;
193    uint32_t startY = 0;
194
195    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
196
197    if (!cacheTexture) {
198        if (!precaching) {
199            // If the new glyph didn't fit and we are not just trying to precache it,
200            // clear out the cache and try again
201            flushAllAndInvalidate();
202            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
203        }
204
205        if (!cacheTexture) {
206            // either the glyph didn't fit or we're precaching and will cache it when we draw
207            return;
208        }
209    }
210
211    cachedGlyph->mCacheTexture = cacheTexture;
212
213    *retOriginX = startX;
214    *retOriginY = startY;
215
216    uint32_t endX = startX + glyph.fWidth;
217    uint32_t endY = startY + glyph.fHeight;
218
219    uint32_t cacheWidth = cacheTexture->getWidth();
220
221    if (!cacheTexture->getTexture()) {
222        Caches::getInstance().activeTexture(0);
223        // Large-glyph texture memory is allocated only as needed
224        cacheTexture->allocateTexture();
225    }
226
227    // Tells us whether the glyphs is B&W (1 bit per pixel)
228    // or anti-aliased (8 bits per pixel)
229    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
230
231    uint8_t* cacheBuffer = cacheTexture->getTexture();
232    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
233
234    // Zero out the borders
235    for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
236        cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
237        cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
238    }
239
240    for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
241            cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
242        cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
243        cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
244    }
245
246    // Copy the glyph image, taking the mask format into account
247    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
248    int stride = glyph.rowBytes();
249
250    switch (format) {
251        case SkMask::kA8_Format: {
252            if (mGammaTable) {
253                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
254                    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
255                        uint8_t tempCol = bitmapBuffer[bY + bX];
256                        cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
257                    }
258                }
259            } else {
260                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
261                    memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY],
262                            glyph.fWidth);
263                }
264            }
265            break;
266        }
267        case SkMask::kBW_Format: {
268            static const uint8_t COLORS[2] = { 0, 255 };
269
270            for (cacheY = startY; cacheY < endY; cacheY++) {
271                cacheX = startX;
272                int rowBytes = stride;
273                uint8_t* buffer = bitmapBuffer;
274
275                while (--rowBytes >= 0) {
276                    uint8_t b = *buffer++;
277                    for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
278                        cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
279                    }
280                }
281
282                bitmapBuffer += stride;
283            }
284            break;
285        }
286        default:
287            ALOGW("Unkown glyph format: 0x%x", format);
288            break;
289    }
290
291    cachedGlyph->mIsValid = true;
292}
293
294CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
295    CacheTexture* cacheTexture = new CacheTexture(width, height);
296
297    if (allocate) {
298        Caches::getInstance().activeTexture(0);
299        cacheTexture->allocateTexture();
300    }
301
302    return cacheTexture;
303}
304
305void FontRenderer::initTextTexture() {
306    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
307        delete mCacheTextures[i];
308    }
309    mCacheTextures.clear();
310
311    mUploadTexture = false;
312    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
313    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
314    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
315    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
316    mCurrentCacheTexture = mCacheTextures[0];
317}
318
319// Avoid having to reallocate memory and render quad by quad
320void FontRenderer::initVertexArrayBuffers() {
321    uint32_t numIndices = mMaxNumberOfQuads * 6;
322    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
323    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
324
325    // Four verts, two triangles , six indices per quad
326    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
327        int i6 = i * 6;
328        int i4 = i * 4;
329
330        indexBufferData[i6 + 0] = i4 + 0;
331        indexBufferData[i6 + 1] = i4 + 1;
332        indexBufferData[i6 + 2] = i4 + 2;
333
334        indexBufferData[i6 + 3] = i4 + 0;
335        indexBufferData[i6 + 4] = i4 + 2;
336        indexBufferData[i6 + 5] = i4 + 3;
337    }
338
339    glGenBuffers(1, &mIndexBufferID);
340    Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
341    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
342
343    free(indexBufferData);
344
345    uint32_t coordSize = 2;
346    uint32_t uvSize = 2;
347    uint32_t vertsPerQuad = 4;
348    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
349    mTextMesh = new float[vertexBufferSize];
350}
351
352// We don't want to allocate anything unless we actually draw text
353void FontRenderer::checkInit() {
354    if (mInitialized) {
355        return;
356    }
357
358    initTextTexture();
359    initVertexArrayBuffers();
360
361    mInitialized = true;
362}
363
364void FontRenderer::updateDrawParams() {
365    if (mCurrentQuadIndex != mLastQuadIndex) {
366        mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6));
367        mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex);
368        mDrawCacheTextures.add(mCurrentCacheTexture);
369        mLastQuadIndex = mCurrentQuadIndex;
370    }
371}
372
373void FontRenderer::checkTextureUpdate() {
374    if (!mUploadTexture) {
375        return;
376    }
377
378    Caches& caches = Caches::getInstance();
379    GLuint lastTextureId = 0;
380    // Iterate over all the cache textures and see which ones need to be updated
381    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
382        CacheTexture* cacheTexture = mCacheTextures[i];
383        if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
384            // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
385            // of data. So expand the dirty rect to the encompassing horizontal stripe.
386            const Rect* dirtyRect = cacheTexture->getDirtyRect();
387            uint32_t x = 0;
388            uint32_t y = dirtyRect->top;
389            uint32_t width = cacheTexture->getWidth();
390            uint32_t height = dirtyRect->getHeight();
391            void* textureData = cacheTexture->getTexture() + y * width;
392
393            if (cacheTexture->getTextureId() != lastTextureId) {
394                lastTextureId = cacheTexture->getTextureId();
395                caches.activeTexture(0);
396                glBindTexture(GL_TEXTURE_2D, lastTextureId);
397            }
398#if DEBUG_FONT_RENDERER
399            ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
400                    i, x, y, width, height);
401#endif
402            glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
403                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
404            cacheTexture->setDirty(false);
405        }
406    }
407
408    mUploadTexture = false;
409}
410
411void FontRenderer::issueDrawCommand() {
412    updateDrawParams();
413    checkTextureUpdate();
414
415    Caches& caches = Caches::getInstance();
416    caches.bindIndicesBuffer(mIndexBufferID);
417    if (!mDrawn) {
418        float* buffer = mTextMesh;
419        int offset = 2;
420
421        bool force = caches.unbindMeshBuffer();
422        caches.bindPositionVertexPointer(force, buffer);
423        caches.bindTexCoordsVertexPointer(force, buffer + offset);
424    }
425
426    for (uint32_t i = 0; i < mDrawOffsets.size(); i++) {
427        uint16_t* offset = mDrawOffsets[i];
428        uint32_t count = mDrawCounts[i];
429        CacheTexture* texture = mDrawCacheTextures[i];
430
431        caches.activeTexture(0);
432        glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
433
434        texture->setLinearFiltering(mLinearFiltering, false);
435
436        glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset);
437    }
438
439    mDrawn = true;
440
441    mCurrentQuadIndex = 0;
442    mLastQuadIndex = 0;
443    mDrawOffsets.clear();
444    mDrawCounts.clear();
445    mDrawCacheTextures.clear();
446}
447
448void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
449        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
450        float x4, float y4, float u4, float v4, CacheTexture* texture) {
451    if (texture != mCurrentCacheTexture) {
452        updateDrawParams();
453        // Now use the new texture id
454        mCurrentCacheTexture = texture;
455    }
456
457    const uint32_t vertsPerQuad = 4;
458    const uint32_t floatsPerVert = 4;
459    float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
460
461    (*currentPos++) = x1;
462    (*currentPos++) = y1;
463    (*currentPos++) = u1;
464    (*currentPos++) = v1;
465
466    (*currentPos++) = x2;
467    (*currentPos++) = y2;
468    (*currentPos++) = u2;
469    (*currentPos++) = v2;
470
471    (*currentPos++) = x3;
472    (*currentPos++) = y3;
473    (*currentPos++) = u3;
474    (*currentPos++) = v3;
475
476    (*currentPos++) = x4;
477    (*currentPos++) = y4;
478    (*currentPos++) = u4;
479    (*currentPos++) = v4;
480
481    mCurrentQuadIndex++;
482}
483
484void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
485        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
486        float x4, float y4, float u4, float v4, CacheTexture* texture) {
487
488    if (mClip &&
489            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
490        return;
491    }
492
493    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
494
495    if (mBounds) {
496        mBounds->left = fmin(mBounds->left, x1);
497        mBounds->top = fmin(mBounds->top, y3);
498        mBounds->right = fmax(mBounds->right, x3);
499        mBounds->bottom = fmax(mBounds->bottom, y1);
500    }
501
502    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
503        issueDrawCommand();
504    }
505}
506
507void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
508        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
509        float x4, float y4, float u4, float v4, CacheTexture* texture) {
510
511    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
512
513    if (mBounds) {
514        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
515        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
516        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
517        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
518    }
519
520    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
521        issueDrawCommand();
522    }
523}
524
525void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
526    mCurrentFont = Font::create(this, paint, matrix);
527}
528
529FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
530        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
531    checkInit();
532
533    if (!mCurrentFont) {
534        DropShadow image;
535        image.width = 0;
536        image.height = 0;
537        image.image = NULL;
538        image.penX = 0;
539        image.penY = 0;
540        return image;
541    }
542
543    mDrawn = false;
544    mClip = NULL;
545    mBounds = NULL;
546
547    Rect bounds;
548    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
549
550    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
551    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
552
553    // Align buffers for renderscript usage
554    if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
555        paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
556    }
557
558    int size = paddedWidth * paddedHeight;
559    uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
560    memset(dataBuffer, 0, size);
561
562    int penX = radius - bounds.left;
563    int penY = radius - bounds.bottom;
564
565    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
566            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
567    blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
568
569    DropShadow image;
570    image.width = paddedWidth;
571    image.height = paddedHeight;
572    image.image = dataBuffer;
573    image.penX = penX;
574    image.penY = penY;
575
576    return image;
577}
578
579void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
580    checkInit();
581
582    mDrawn = false;
583    mBounds = bounds;
584    mClip = clip;
585}
586
587void FontRenderer::finishRender() {
588    mBounds = NULL;
589    mClip = NULL;
590
591    if (mCurrentQuadIndex != 0) {
592        issueDrawCommand();
593    }
594}
595
596void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
597    Font* font = Font::create(this, paint, matrix);
598    font->precache(paint, text, numGlyphs);
599}
600
601bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
602        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
603        const float* positions, Rect* bounds) {
604    if (!mCurrentFont) {
605        ALOGE("No font set");
606        return false;
607    }
608
609    initRender(clip, bounds);
610    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
611    finishRender();
612
613    return mDrawn;
614}
615
616bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
617        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
618        float hOffset, float vOffset, Rect* bounds) {
619    if (!mCurrentFont) {
620        ALOGE("No font set");
621        return false;
622    }
623
624    initRender(clip, bounds);
625    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
626    finishRender();
627
628    return mDrawn;
629}
630
631void FontRenderer::removeFont(const Font* font) {
632    mActiveFonts.remove(font->getDescription());
633
634    if (mCurrentFont == font) {
635        mCurrentFont = NULL;
636    }
637}
638
639void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
640    // Compute gaussian weights for the blur
641    // e is the euler's number
642    float e = 2.718281828459045f;
643    float pi = 3.1415926535897932f;
644    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
645    // x is of the form [-radius .. 0 .. radius]
646    // and sigma varies with radius.
647    // Based on some experimental radius values and sigma's
648    // we approximately fit sigma = f(radius) as
649    // sigma = radius * 0.3  + 0.6
650    // The larger the radius gets, the more our gaussian blur
651    // will resemble a box blur since with large sigma
652    // the gaussian curve begins to lose its shape
653    float sigma = 0.3f * (float) radius + 0.6f;
654
655    // Now compute the coefficints
656    // We will store some redundant values to save some math during
657    // the blur calculations
658    // precompute some values
659    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
660    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
661
662    float normalizeFactor = 0.0f;
663    for (int32_t r = -radius; r <= radius; r ++) {
664        float floatR = (float) r;
665        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
666        normalizeFactor += weights[r + radius];
667    }
668
669    //Now we need to normalize the weights because all our coefficients need to add up to one
670    normalizeFactor = 1.0f / normalizeFactor;
671    for (int32_t r = -radius; r <= radius; r ++) {
672        weights[r + radius] *= normalizeFactor;
673    }
674}
675
676void FontRenderer::horizontalBlur(float* weights, int32_t radius,
677        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
678    float blurredPixel = 0.0f;
679    float currentPixel = 0.0f;
680
681    for (int32_t y = 0; y < height; y ++) {
682
683        const uint8_t* input = source + y * width;
684        uint8_t* output = dest + y * width;
685
686        for (int32_t x = 0; x < width; x ++) {
687            blurredPixel = 0.0f;
688            const float* gPtr = weights;
689            // Optimization for non-border pixels
690            if (x > radius && x < (width - radius)) {
691                const uint8_t *i = input + (x - radius);
692                for (int r = -radius; r <= radius; r ++) {
693                    currentPixel = (float) (*i);
694                    blurredPixel += currentPixel * gPtr[0];
695                    gPtr++;
696                    i++;
697                }
698            } else {
699                for (int32_t r = -radius; r <= radius; r ++) {
700                    // Stepping left and right away from the pixel
701                    int validW = x + r;
702                    if (validW < 0) {
703                        validW = 0;
704                    }
705                    if (validW > width - 1) {
706                        validW = width - 1;
707                    }
708
709                    currentPixel = (float) input[validW];
710                    blurredPixel += currentPixel * gPtr[0];
711                    gPtr++;
712                }
713            }
714            *output = (uint8_t)blurredPixel;
715            output ++;
716        }
717    }
718}
719
720void FontRenderer::verticalBlur(float* weights, int32_t radius,
721        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
722    float blurredPixel = 0.0f;
723    float currentPixel = 0.0f;
724
725    for (int32_t y = 0; y < height; y ++) {
726        uint8_t* output = dest + y * width;
727
728        for (int32_t x = 0; x < width; x ++) {
729            blurredPixel = 0.0f;
730            const float* gPtr = weights;
731            const uint8_t* input = source + x;
732            // Optimization for non-border pixels
733            if (y > radius && y < (height - radius)) {
734                const uint8_t *i = input + ((y - radius) * width);
735                for (int32_t r = -radius; r <= radius; r ++) {
736                    currentPixel = (float)(*i);
737                    blurredPixel += currentPixel * gPtr[0];
738                    gPtr++;
739                    i += width;
740                }
741            } else {
742                for (int32_t r = -radius; r <= radius; r ++) {
743                    int validH = y + r;
744                    // Clamp to zero and width
745                    if (validH < 0) {
746                        validH = 0;
747                    }
748                    if (validH > height - 1) {
749                        validH = height - 1;
750                    }
751
752                    const uint8_t *i = input + validH * width;
753                    currentPixel = (float) (*i);
754                    blurredPixel += currentPixel * gPtr[0];
755                    gPtr++;
756                }
757            }
758            *output = (uint8_t) blurredPixel;
759            output++;
760        }
761    }
762}
763
764void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
765    if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
766        float *gaussian = new float[2 * radius + 1];
767        computeGaussianWeights(gaussian, radius);
768
769        uint8_t* scratch = new uint8_t[width * height];
770
771        horizontalBlur(gaussian, radius, *image, scratch, width, height);
772        verticalBlur(gaussian, radius, scratch, *image, width, height);
773
774        delete[] gaussian;
775        delete[] scratch;
776    }
777
778    uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
779
780    if (mRs.get() == 0) {
781        mRs = new RSC::RS();
782        if (!mRs->init(true, true)) {
783            ALOGE("blur RS failed to init");
784        }
785
786        mRsElement = RSC::Element::A_8(mRs);
787        mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
788    }
789
790    sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
791    sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
792            RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
793    sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
794            RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
795
796    mRsScript->setRadius(radius);
797    mRsScript->blur(ain, aout);
798
799    // replace the original image's pointer, avoiding a copy back to the original buffer
800    free(*image);
801    *image = outImage;
802}
803
804}; // namespace uirenderer
805}; // namespace android
806