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#include "FontRenderer.h"
18
19#include "BakedOpDispatcher.h"
20#include "BakedOpRenderer.h"
21#include "BakedOpState.h"
22#include "Caches.h"
23#include "Debug.h"
24#include "Extensions.h"
25#include "font/Font.h"
26#include "Glop.h"
27#include "GlopBuilder.h"
28#include "PixelBuffer.h"
29#include "Rect.h"
30#include "renderstate/RenderState.h"
31#include "utils/Blur.h"
32#include "utils/Timing.h"
33
34#include <algorithm>
35#include <cutils/properties.h>
36#include <RenderScript.h>
37#include <SkGlyph.h>
38#include <SkUtils.h>
39#include <utils/Log.h>
40
41namespace android {
42namespace uirenderer {
43
44// blur inputs smaller than this constant will bypass renderscript
45#define RS_MIN_INPUT_CUTOFF 10000
46
47///////////////////////////////////////////////////////////////////////////////
48// TextSetupFunctor
49///////////////////////////////////////////////////////////////////////////////
50
51void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
52    int textureFillFlags = TextureFillFlags::None;
53    if (texture.getFormat() == GL_ALPHA) {
54        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
55    }
56    if (linearFiltering) {
57        textureFillFlags |= TextureFillFlags::ForceFilter;
58    }
59    int transformFlags = pureTranslate
60            ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
61#ifdef ANDROID_ENABLE_LINEAR_BLENDING
62    bool gammaCorrection = true;
63#else
64    bool gammaCorrection = false;
65#endif
66    Glop glop;
67    GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
68            .setRoundRectClipState(bakedState->roundRectClipState)
69            .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
70            .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
71            .setGammaCorrection(gammaCorrection)
72            .setTransform(bakedState->computedState.transform, transformFlags)
73            .setModelViewIdentityEmptyBounds()
74            .build();
75    // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
76    renderer->renderGlop(nullptr, clip, glop);
77}
78
79///////////////////////////////////////////////////////////////////////////////
80// FontRenderer
81///////////////////////////////////////////////////////////////////////////////
82
83static bool sLogFontRendererCreate = true;
84
85FontRenderer::FontRenderer(const uint8_t* gammaTable)
86        : mGammaTable(gammaTable)
87        , mCurrentFont(nullptr)
88        , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
89        , mCurrentCacheTexture(nullptr)
90        , mUploadTexture(false)
91        , mFunctor(nullptr)
92        , mClip(nullptr)
93        , mBounds(nullptr)
94        , mDrawn(false)
95        , mInitialized(false)
96        , mLinearFiltering(false) {
97
98    if (sLogFontRendererCreate) {
99        INIT_LOGD("Creating FontRenderer");
100    }
101
102    mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH,
103            DEFAULT_TEXT_SMALL_CACHE_WIDTH);
104    mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT,
105            DEFAULT_TEXT_SMALL_CACHE_HEIGHT);
106
107    mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH,
108            DEFAULT_TEXT_LARGE_CACHE_WIDTH);
109    mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT,
110            DEFAULT_TEXT_LARGE_CACHE_HEIGHT);
111
112    uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
113
114    mSmallCacheWidth = std::min(mSmallCacheWidth, maxTextureSize);
115    mSmallCacheHeight = std::min(mSmallCacheHeight, maxTextureSize);
116    mLargeCacheWidth = std::min(mLargeCacheWidth, maxTextureSize);
117    mLargeCacheHeight = std::min(mLargeCacheHeight, maxTextureSize);
118
119    if (sLogFontRendererCreate) {
120        INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
121                mSmallCacheWidth, mSmallCacheHeight,
122                mLargeCacheWidth, mLargeCacheHeight >> 1,
123                mLargeCacheWidth, mLargeCacheHeight >> 1,
124                mLargeCacheWidth, mLargeCacheHeight);
125    }
126
127    sLogFontRendererCreate = false;
128}
129
130void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) {
131    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
132        delete cacheTextures[i];
133    }
134    cacheTextures.clear();
135}
136
137FontRenderer::~FontRenderer() {
138    clearCacheTextures(mACacheTextures);
139    clearCacheTextures(mRGBACacheTextures);
140
141    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
142    while (it.next()) {
143        delete it.value();
144    }
145    mActiveFonts.clear();
146}
147
148void FontRenderer::flushAllAndInvalidate() {
149    issueDrawCommand();
150
151    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
152    while (it.next()) {
153        it.value()->invalidateTextureCache();
154    }
155
156    for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
157        mACacheTextures[i]->init();
158
159#ifdef BUGREPORT_FONT_CACHE_USAGE
160        mHistoryTracker.glyphsCleared(mACacheTextures[i]);
161#endif
162    }
163
164    for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
165        mRGBACacheTextures[i]->init();
166#ifdef BUGREPORT_FONT_CACHE_USAGE
167        mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]);
168#endif
169    }
170
171    mDrawn = false;
172}
173
174void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) {
175    // Start from 1; don't deallocate smallest/default texture
176    for (uint32_t i = 1; i < cacheTextures.size(); i++) {
177        CacheTexture* cacheTexture = cacheTextures[i];
178        if (cacheTexture->getPixelBuffer()) {
179            cacheTexture->init();
180#ifdef BUGREPORT_FONT_CACHE_USAGE
181            mHistoryTracker.glyphsCleared(cacheTexture);
182#endif
183            LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
184            while (it.next()) {
185                it.value()->invalidateTextureCache(cacheTexture);
186            }
187            cacheTexture->releasePixelBuffer();
188        }
189    }
190}
191
192void FontRenderer::flushLargeCaches() {
193    flushLargeCaches(mACacheTextures);
194    flushLargeCaches(mRGBACacheTextures);
195}
196
197CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures,
198        const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
199    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
200        if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
201            return cacheTextures[i];
202        }
203    }
204    // Could not fit glyph into current cache textures
205    return nullptr;
206}
207
208void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
209        uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
210    checkInit();
211
212    // If the glyph bitmap is empty let's assum the glyph is valid
213    // so we can avoid doing extra work later on
214    if (glyph.fWidth == 0 || glyph.fHeight == 0) {
215        cachedGlyph->mIsValid = true;
216        cachedGlyph->mCacheTexture = nullptr;
217        return;
218    }
219
220    cachedGlyph->mIsValid = false;
221
222    // choose an appropriate cache texture list for this glyph format
223    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
224    std::vector<CacheTexture*>* cacheTextures = nullptr;
225    switch (format) {
226        case SkMask::kA8_Format:
227        case SkMask::kBW_Format:
228            cacheTextures = &mACacheTextures;
229            break;
230        case SkMask::kARGB32_Format:
231            cacheTextures = &mRGBACacheTextures;
232            break;
233        default:
234#if DEBUG_FONT_RENDERER
235            ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
236#endif
237        return;
238    }
239
240    // If the glyph is too tall, don't cache it
241    if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
242                (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
243        ALOGE("Font size too large to fit in cache. width, height = %i, %i",
244                (int) glyph.fWidth, (int) glyph.fHeight);
245        return;
246    }
247
248    // Now copy the bitmap into the cache texture
249    uint32_t startX = 0;
250    uint32_t startY = 0;
251
252    CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
253
254    if (!cacheTexture) {
255        if (!precaching) {
256            // If the new glyph didn't fit and we are not just trying to precache it,
257            // clear out the cache and try again
258            flushAllAndInvalidate();
259            cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
260        }
261
262        if (!cacheTexture) {
263            // either the glyph didn't fit or we're precaching and will cache it when we draw
264            return;
265        }
266    }
267
268    cachedGlyph->mCacheTexture = cacheTexture;
269
270    *retOriginX = startX;
271    *retOriginY = startY;
272
273    uint32_t endX = startX + glyph.fWidth;
274    uint32_t endY = startY + glyph.fHeight;
275
276    uint32_t cacheWidth = cacheTexture->getWidth();
277
278    if (!cacheTexture->getPixelBuffer()) {
279        Caches::getInstance().textureState().activateTexture(0);
280        // Large-glyph texture memory is allocated only as needed
281        cacheTexture->allocatePixelBuffer();
282    }
283    if (!cacheTexture->mesh()) {
284        cacheTexture->allocateMesh();
285    }
286
287    uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
288    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
289    int srcStride = glyph.rowBytes();
290
291    // Copy the glyph image, taking the mask format into account
292    switch (format) {
293        case SkMask::kA8_Format: {
294            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
295                    - TEXTURE_BORDER_SIZE;
296            // write leading border line
297            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
298            // write glyph data
299            if (mGammaTable) {
300                for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
301                    row = cacheY * cacheWidth;
302                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
303                    for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
304                        uint8_t tempCol = bitmapBuffer[bY + bX];
305                        cacheBuffer[row + cacheX] = mGammaTable[tempCol];
306                    }
307                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
308                }
309            } else {
310                for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
311                    row = cacheY * cacheWidth;
312                    memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
313                    cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
314                    cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
315                }
316            }
317            // write trailing border line
318            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
319            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
320            break;
321        }
322        case SkMask::kARGB32_Format: {
323            // prep data lengths
324            const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
325            const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
326            size_t rowSize = formatSize * glyph.fWidth;
327            // prep advances
328            size_t dstStride = formatSize * cacheWidth;
329            // prep indices
330            // - we actually start one row early, and then increment before first copy
331            uint8_t* src = &bitmapBuffer[0 - srcStride];
332            uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
333            uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
334            uint8_t* dstL = dst - borderSize;
335            uint8_t* dstR = dst + rowSize;
336            // write leading border line
337            memset(dstL, 0, rowSize + 2 * borderSize);
338            // write glyph data
339            while (dst < dstEnd) {
340                memset(dstL += dstStride, 0, borderSize); // leading border column
341                memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
342                memset(dstR += dstStride, 0, borderSize); // trailing border column
343            }
344            // write trailing border line
345            memset(dstL += dstStride, 0, rowSize + 2 * borderSize);
346            break;
347        }
348        case SkMask::kBW_Format: {
349            uint32_t cacheX = 0, cacheY = 0;
350            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
351                    - TEXTURE_BORDER_SIZE;
352            static const uint8_t COLORS[2] = { 0, 255 };
353            // write leading border line
354            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
355            // write glyph data
356            for (cacheY = startY; cacheY < endY; cacheY++) {
357                cacheX = startX;
358                int rowBytes = srcStride;
359                uint8_t* buffer = bitmapBuffer;
360
361                row = cacheY * cacheWidth;
362                cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
363                while (--rowBytes >= 0) {
364                    uint8_t b = *buffer++;
365                    for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
366                        cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
367                    }
368                }
369                cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
370
371                bitmapBuffer += srcStride;
372            }
373            // write trailing border line
374            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
375            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
376            break;
377        }
378        default:
379            ALOGW("Unknown glyph format: 0x%x", format);
380            break;
381    }
382
383    cachedGlyph->mIsValid = true;
384
385#ifdef BUGREPORT_FONT_CACHE_USAGE
386    mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight);
387#endif
388}
389
390CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
391        bool allocate) {
392    CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
393
394    if (allocate) {
395        Caches::getInstance().textureState().activateTexture(0);
396        cacheTexture->allocatePixelBuffer();
397        cacheTexture->allocateMesh();
398    }
399
400    return cacheTexture;
401}
402
403void FontRenderer::initTextTexture() {
404    clearCacheTextures(mACacheTextures);
405    clearCacheTextures(mRGBACacheTextures);
406
407    mUploadTexture = false;
408    mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
409            GL_ALPHA, true));
410    mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
411            GL_ALPHA, false));
412    mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
413            GL_ALPHA, false));
414    mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
415            GL_ALPHA, false));
416    mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
417            GL_RGBA, false));
418    mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
419            GL_RGBA, false));
420    mCurrentCacheTexture = mACacheTextures[0];
421}
422
423// We don't want to allocate anything unless we actually draw text
424void FontRenderer::checkInit() {
425    if (mInitialized) {
426        return;
427    }
428
429    initTextTexture();
430
431    mInitialized = true;
432}
433
434void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures,
435        bool& resetPixelStore, GLuint& lastTextureId) {
436    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
437        CacheTexture* cacheTexture = cacheTextures[i];
438        if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
439            if (cacheTexture->getTextureId() != lastTextureId) {
440                lastTextureId = cacheTexture->getTextureId();
441                caches.textureState().activateTexture(0);
442                caches.textureState().bindTexture(lastTextureId);
443            }
444
445            if (cacheTexture->upload()) {
446                resetPixelStore = true;
447            }
448        }
449    }
450}
451
452void FontRenderer::checkTextureUpdate() {
453    if (!mUploadTexture) {
454        return;
455    }
456
457    Caches& caches = Caches::getInstance();
458    GLuint lastTextureId = 0;
459
460    bool resetPixelStore = false;
461
462    // Iterate over all the cache textures and see which ones need to be updated
463    checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
464    checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
465
466    // Unbind any PBO we might have used to update textures
467    caches.pixelBufferState().unbind();
468
469    // Reset to default unpack row length to avoid affecting texture
470    // uploads in other parts of the renderer
471    if (resetPixelStore) {
472        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
473    }
474
475    mUploadTexture = false;
476}
477
478void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) {
479    if (!mFunctor) return;
480
481    bool first = true;
482    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
483        CacheTexture* texture = cacheTextures[i];
484        if (texture->canDraw()) {
485            if (first) {
486                checkTextureUpdate();
487                first = false;
488                mDrawn = true;
489            }
490
491            mFunctor->draw(*texture, mLinearFiltering);
492
493            texture->resetMesh();
494        }
495    }
496}
497
498void FontRenderer::issueDrawCommand() {
499    issueDrawCommand(mACacheTextures);
500    issueDrawCommand(mRGBACacheTextures);
501}
502
503void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
504        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
505        float x4, float y4, float u4, float v4, CacheTexture* texture) {
506    if (texture != mCurrentCacheTexture) {
507        // Now use the new texture id
508        mCurrentCacheTexture = texture;
509    }
510
511    mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
512            x3, y3, u3, v3, x4, y4, u4, v4);
513}
514
515void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
516        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
517        float x4, float y4, float u4, float v4, CacheTexture* texture) {
518
519    if (mClip &&
520            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
521        return;
522    }
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 = std::min(mBounds->left, x1);
528        mBounds->top = std::min(mBounds->top, y3);
529        mBounds->right = std::max(mBounds->right, x3);
530        mBounds->bottom = std::max(mBounds->bottom, y1);
531    }
532
533    if (mCurrentCacheTexture->endOfMesh()) {
534        issueDrawCommand();
535    }
536}
537
538void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
539        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
540        float x4, float y4, float u4, float v4, CacheTexture* texture) {
541
542    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
543
544    if (mBounds) {
545        mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4))));
546        mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4))));
547        mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4))));
548        mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4))));
549    }
550
551    if (mCurrentCacheTexture->endOfMesh()) {
552        issueDrawCommand();
553    }
554}
555
556void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
557    mCurrentFont = Font::create(this, paint, matrix);
558}
559
560FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
561        int numGlyphs, float radius, const float* positions) {
562    checkInit();
563
564    DropShadow image;
565    image.width = 0;
566    image.height = 0;
567    image.image = nullptr;
568    image.penX = 0;
569    image.penY = 0;
570
571    if (!mCurrentFont) {
572        return image;
573    }
574
575    mDrawn = false;
576    mClip = nullptr;
577    mBounds = nullptr;
578
579    Rect bounds;
580    mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
581
582    uint32_t intRadius = Blur::convertRadiusToInt(radius);
583    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
584    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
585
586    uint32_t maxSize = Caches::getInstance().maxTextureSize;
587    if (paddedWidth > maxSize || paddedHeight > maxSize) {
588        return image;
589    }
590
591    // Align buffers for renderscript usage
592    if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
593        paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
594    }
595    int size = paddedWidth * paddedHeight;
596    uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
597
598    memset(dataBuffer, 0, size);
599
600    int penX = intRadius - bounds.left;
601    int penY = intRadius - bounds.bottom;
602
603    if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
604        // text has non-whitespace, so draw and blur to create the shadow
605        // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
606        // TODO: don't draw pure whitespace in the first place, and avoid needing this check
607        mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
608                Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
609
610        // Unbind any PBO we might have used
611        Caches::getInstance().pixelBufferState().unbind();
612
613        blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
614    }
615
616    image.width = paddedWidth;
617    image.height = paddedHeight;
618    image.image = dataBuffer;
619    image.penX = penX;
620    image.penY = penY;
621
622    return image;
623}
624
625void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
626    checkInit();
627
628    mDrawn = false;
629    mBounds = bounds;
630    mFunctor = functor;
631    mClip = clip;
632}
633
634void FontRenderer::finishRender() {
635    mBounds = nullptr;
636    mClip = nullptr;
637
638    issueDrawCommand();
639}
640
641void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
642        const SkMatrix& matrix) {
643    Font* font = Font::create(this, paint, matrix);
644    font->precache(paint, glyphs, numGlyphs);
645}
646
647void FontRenderer::endPrecaching() {
648    checkTextureUpdate();
649}
650
651bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
652        int numGlyphs, int x, int y, const float* positions,
653        Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
654    if (!mCurrentFont) {
655        ALOGE("No font set");
656        return false;
657    }
658
659    initRender(clip, bounds, functor);
660    mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
661
662    if (forceFinish) {
663        finishRender();
664    }
665
666    return mDrawn;
667}
668
669bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
670        int numGlyphs, const SkPath* path, float hOffset, float vOffset,
671        Rect* bounds, TextDrawFunctor* functor) {
672    if (!mCurrentFont) {
673        ALOGE("No font set");
674        return false;
675    }
676
677    initRender(clip, bounds, functor);
678    mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
679    finishRender();
680
681    return mDrawn;
682}
683
684void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
685    uint32_t intRadius = Blur::convertRadiusToInt(radius);
686    if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
687        uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
688
689        if (mRs == nullptr) {
690            mRs = new RSC::RS();
691            // a null path is OK because there are no custom kernels used
692            // hence nothing gets cached by RS
693            if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
694                mRs.clear();
695                ALOGE("blur RS failed to init");
696            } else {
697                mRsElement = RSC::Element::A_8(mRs);
698                mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
699            }
700        }
701        if (mRs != nullptr) {
702            RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
703            RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
704                    RS_ALLOCATION_MIPMAP_NONE,
705                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
706                    *image);
707            RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t,
708                    RS_ALLOCATION_MIPMAP_NONE,
709                    RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
710                    outImage);
711
712            mRsScript->setRadius(radius);
713            mRsScript->setInput(ain);
714            mRsScript->forEach(aout);
715
716            // replace the original image's pointer, avoiding a copy back to the original buffer
717            free(*image);
718            *image = outImage;
719
720            return;
721        }
722    }
723
724    std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
725    Blur::generateGaussianWeights(gaussian.get(), radius);
726
727    std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
728    Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
729    Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
730}
731
732static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
733    uint32_t size = 0;
734    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
735        CacheTexture* cacheTexture = cacheTextures[i];
736        if (cacheTexture && cacheTexture->getPixelBuffer()) {
737            size += cacheTexture->getPixelBuffer()->getSize();
738        }
739    }
740    return size;
741}
742
743static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
744    uint32_t size = 0;
745    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
746        CacheTexture* cacheTexture = cacheTextures[i];
747        if (cacheTexture && cacheTexture->getPixelBuffer()) {
748            size += cacheTexture->calculateFreeMemory();
749        }
750    }
751    return size;
752}
753
754const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const {
755    switch (format) {
756        case GL_ALPHA: {
757            return mACacheTextures;
758        }
759        case GL_RGBA: {
760            return mRGBACacheTextures;
761        }
762        default: {
763            LOG_ALWAYS_FATAL("Unsupported format: %d", format);
764            // Impossible to hit this, but the compiler doesn't know that
765            return *(new std::vector<CacheTexture*>());
766        }
767    }
768}
769
770static void dumpTextures(String8& log, const char* tag,
771        const std::vector<CacheTexture*>& cacheTextures) {
772    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
773        CacheTexture* cacheTexture = cacheTextures[i];
774        if (cacheTexture && cacheTexture->getPixelBuffer()) {
775            uint32_t free = cacheTexture->calculateFreeMemory();
776            uint32_t total = cacheTexture->getPixelBuffer()->getSize();
777            log.appendFormat("    %-4s texture %d     %8d / %8d\n", tag, i, total - free, total);
778        }
779    }
780}
781
782void FontRenderer::dumpMemoryUsage(String8& log) const {
783    const uint32_t sizeA8 = getCacheSize(GL_ALPHA);
784    const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA);
785    const uint32_t sizeRGBA = getCacheSize(GL_RGBA);
786    const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA);
787    log.appendFormat("  FontRenderer A8      %8d / %8d\n", usedA8, sizeA8);
788    dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA));
789    log.appendFormat("  FontRenderer RGBA    %8d / %8d\n", usedRGBA, sizeRGBA);
790    dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA));
791    log.appendFormat("  FontRenderer total   %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA);
792}
793
794uint32_t FontRenderer::getCacheSize(GLenum format) const {
795    return calculateCacheSize(cacheTexturesForFormat(format));
796}
797
798uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
799    return calculateFreeCacheSize(cacheTexturesForFormat(format));
800}
801
802uint32_t FontRenderer::getSize() const {
803    return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
804}
805
806}; // namespace uirenderer
807}; // namespace android
808