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