• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
49 namespace android {
50 namespace 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 
draw(CacheTexture & texture,bool linearFiltering)59 void 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 
96 static bool sLogFontRendererCreate = true;
97 
FontRenderer(const uint8_t * gammaTable)98 FontRenderer::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 
clearCacheTextures(std::vector<CacheTexture * > & cacheTextures)143 void 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 
~FontRenderer()150 FontRenderer::~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 
flushAllAndInvalidate()161 void 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 
flushLargeCaches(std::vector<CacheTexture * > & cacheTextures)187 void 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 
flushLargeCaches()205 void FontRenderer::flushLargeCaches() {
206     flushLargeCaches(mACacheTextures);
207     flushLargeCaches(mRGBACacheTextures);
208 }
209 
cacheBitmapInTexture(std::vector<CacheTexture * > & cacheTextures,const SkGlyph & glyph,uint32_t * startX,uint32_t * startY)210 CacheTexture* 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 
cacheBitmap(const SkGlyph & glyph,CachedGlyphInfo * cachedGlyph,uint32_t * retOriginX,uint32_t * retOriginY,bool precaching)221 void 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 
createCacheTexture(int width,int height,GLenum format,bool allocate)404 CacheTexture* 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 
initTextTexture()417 void 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
checkInit()438 void FontRenderer::checkInit() {
439     if (mInitialized) {
440         return;
441     }
442 
443     initTextTexture();
444 
445     mInitialized = true;
446 }
447 
checkTextureUpdateForCache(Caches & caches,std::vector<CacheTexture * > & cacheTextures,bool & resetPixelStore,GLuint & lastTextureId)448 void 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 
checkTextureUpdate()466 void 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 
issueDrawCommand(std::vector<CacheTexture * > & cacheTextures)492 void 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 
issueDrawCommand()512 void FontRenderer::issueDrawCommand() {
513     issueDrawCommand(mACacheTextures);
514     issueDrawCommand(mRGBACacheTextures);
515 }
516 
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)517 void 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 
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)529 void 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 
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)552 void 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 
setFont(const SkPaint * paint,const SkMatrix & matrix)570 void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
571     mCurrentFont = Font::create(this, paint, matrix);
572 }
573 
renderDropShadow(const SkPaint * paint,const glyph_t * glyphs,int numGlyphs,float radius,const float * positions)574 FontRenderer::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 
initRender(const Rect * clip,Rect * bounds,TextDrawFunctor * functor)644 void 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 
finishRender()653 void FontRenderer::finishRender() {
654     mBounds = nullptr;
655     mClip = nullptr;
656 
657     issueDrawCommand();
658 }
659 
precache(const SkPaint * paint,const glyph_t * glyphs,int numGlyphs,const SkMatrix & matrix)660 void 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 
endPrecaching()666 void FontRenderer::endPrecaching() {
667     checkTextureUpdate();
668 }
669 
renderPosText(const SkPaint * paint,const Rect * clip,const glyph_t * glyphs,int numGlyphs,int x,int y,const float * positions,Rect * bounds,TextDrawFunctor * functor,bool forceFinish)670 bool 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 
renderTextOnPath(const SkPaint * paint,const Rect * clip,const glyph_t * glyphs,int numGlyphs,const SkPath * path,float hOffset,float vOffset,Rect * bounds,TextDrawFunctor * functor)688 bool 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 
blurImage(uint8_t ** image,int32_t width,int32_t height,float radius)703 void 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 
calculateCacheSize(const std::vector<CacheTexture * > & cacheTextures)753 static 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 
calculateFreeCacheSize(const std::vector<CacheTexture * > & cacheTextures)764 static 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 
cacheTexturesForFormat(GLenum format) const775 const 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 
dumpTextures(String8 & log,const char * tag,const std::vector<CacheTexture * > & cacheTextures)791 static 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 
dumpMemoryUsage(String8 & log) const803 void 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 
getCacheSize(GLenum format) const815 uint32_t FontRenderer::getCacheSize(GLenum format) const {
816     return calculateCacheSize(cacheTexturesForFormat(format));
817 }
818 
getFreeCacheSize(GLenum format) const819 uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
820     return calculateFreeCacheSize(cacheTexturesForFormat(format));
821 }
822 
getSize() const823 uint32_t FontRenderer::getSize() const {
824     return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
825 }
826 
827 }; // namespace uirenderer
828 }; // namespace android
829