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