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