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