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