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