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