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