• 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/Functor.h>
25 #include <utils/Log.h>
26 
27 #include <RenderScript.h>
28 
29 #include "utils/Blur.h"
30 #include "utils/Timing.h"
31 
32 #include "Caches.h"
33 #include "Debug.h"
34 #include "Extensions.h"
35 #include "FontRenderer.h"
36 #include "PixelBuffer.h"
37 #include "Rect.h"
38 
39 namespace android {
40 namespace uirenderer {
41 
42 // blur inputs smaller than this constant will bypass renderscript
43 #define RS_MIN_INPUT_CUTOFF 10000
44 
45 ///////////////////////////////////////////////////////////////////////////////
46 // FontRenderer
47 ///////////////////////////////////////////////////////////////////////////////
48 
49 static bool sLogFontRendererCreate = true;
50 
FontRenderer()51 FontRenderer::FontRenderer() :
52         mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
53 
54     if (sLogFontRendererCreate) {
55         INIT_LOGD("Creating FontRenderer");
56     }
57 
58     mGammaTable = NULL;
59     mInitialized = false;
60 
61     mCurrentCacheTexture = NULL;
62 
63     mLinearFiltering = false;
64 
65     mIndexBufferID = 0;
66 
67     mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
68     mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
69     mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
70     mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
71 
72     char property[PROPERTY_VALUE_MAX];
73     if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
74         mSmallCacheWidth = atoi(property);
75     }
76 
77     if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
78         mSmallCacheHeight = atoi(property);
79     }
80 
81     if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
82         mLargeCacheWidth = atoi(property);
83     }
84 
85     if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
86         mLargeCacheHeight = atoi(property);
87     }
88 
89     uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
90     mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
91     mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
92     mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
93     mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
94 
95     if (sLogFontRendererCreate) {
96         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
97                 mSmallCacheWidth, mSmallCacheHeight,
98                 mLargeCacheWidth, mLargeCacheHeight >> 1,
99                 mLargeCacheWidth, mLargeCacheHeight >> 1,
100                 mLargeCacheWidth, mLargeCacheHeight);
101     }
102 
103     sLogFontRendererCreate = false;
104 }
105 
~FontRenderer()106 FontRenderer::~FontRenderer() {
107     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
108         delete mCacheTextures[i];
109     }
110     mCacheTextures.clear();
111 
112     if (mInitialized) {
113         // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
114         Caches::getInstance().unbindIndicesBuffer();
115         glDeleteBuffers(1, &mIndexBufferID);
116     }
117 
118     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
119     while (it.next()) {
120         delete it.value();
121     }
122     mActiveFonts.clear();
123 }
124 
flushAllAndInvalidate()125 void FontRenderer::flushAllAndInvalidate() {
126     issueDrawCommand();
127 
128     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
129     while (it.next()) {
130         it.value()->invalidateTextureCache();
131     }
132 
133     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
134         mCacheTextures[i]->init();
135     }
136 }
137 
flushLargeCaches()138 void FontRenderer::flushLargeCaches() {
139     // Start from 1; don't deallocate smallest/default texture
140     for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
141         CacheTexture* cacheTexture = mCacheTextures[i];
142         if (cacheTexture->getPixelBuffer()) {
143             cacheTexture->init();
144             LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
145             while (it.next()) {
146                 it.value()->invalidateTextureCache(cacheTexture);
147             }
148             cacheTexture->releaseTexture();
149         }
150     }
151 }
152 
cacheBitmapInTexture(const SkGlyph & glyph,uint32_t * startX,uint32_t * startY)153 CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
154         uint32_t* startX, uint32_t* startY) {
155     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
156         if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
157             return mCacheTextures[i];
158         }
159     }
160     // Could not fit glyph into current cache textures
161     return NULL;
162 }
163 
cacheBitmap(const SkGlyph & glyph,CachedGlyphInfo * cachedGlyph,uint32_t * retOriginX,uint32_t * retOriginY,bool precaching)164 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
165         uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
166     checkInit();
167 
168     // If the glyph bitmap is empty let's assum the glyph is valid
169     // so we can avoid doing extra work later on
170     if (glyph.fWidth == 0 || glyph.fHeight == 0) {
171         cachedGlyph->mIsValid = true;
172         cachedGlyph->mCacheTexture = NULL;
173         return;
174     }
175 
176     cachedGlyph->mIsValid = false;
177 
178     // If the glyph is too tall, don't cache it
179     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
180                 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
181         ALOGE("Font size too large to fit in cache. width, height = %i, %i",
182                 (int) glyph.fWidth, (int) glyph.fHeight);
183         return;
184     }
185 
186     // Now copy the bitmap into the cache texture
187     uint32_t startX = 0;
188     uint32_t startY = 0;
189 
190     CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
191 
192     if (!cacheTexture) {
193         if (!precaching) {
194             // If the new glyph didn't fit and we are not just trying to precache it,
195             // clear out the cache and try again
196             flushAllAndInvalidate();
197             cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
198         }
199 
200         if (!cacheTexture) {
201             // either the glyph didn't fit or we're precaching and will cache it when we draw
202             return;
203         }
204     }
205 
206     cachedGlyph->mCacheTexture = cacheTexture;
207 
208     *retOriginX = startX;
209     *retOriginY = startY;
210 
211     uint32_t endX = startX + glyph.fWidth;
212     uint32_t endY = startY + glyph.fHeight;
213 
214     uint32_t cacheWidth = cacheTexture->getWidth();
215 
216     if (!cacheTexture->getPixelBuffer()) {
217         Caches::getInstance().activeTexture(0);
218         // Large-glyph texture memory is allocated only as needed
219         cacheTexture->allocateTexture();
220     }
221     if (!cacheTexture->mesh()) {
222         cacheTexture->allocateMesh();
223     }
224 
225     // Tells us whether the glyphs is B&W (1 bit per pixel)
226     // or anti-aliased (8 bits per pixel)
227     SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
228 
229     uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
230     uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
231 
232     // Copy the glyph image, taking the mask format into account
233     uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
234     int stride = glyph.rowBytes();
235 
236     uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
237     memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
238 
239     switch (format) {
240         case SkMask::kA8_Format: {
241             if (mGammaTable) {
242                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
243                     row = cacheY * cacheWidth;
244                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
245                     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
246                         uint8_t tempCol = bitmapBuffer[bY + bX];
247                         cacheBuffer[row + cacheX] = mGammaTable[tempCol];
248                     }
249                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
250                 }
251             } else {
252                 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
253                     row = cacheY * cacheWidth;
254                     memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
255                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
256                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
257                 }
258             }
259             break;
260         }
261         case SkMask::kBW_Format: {
262             static const uint8_t COLORS[2] = { 0, 255 };
263 
264             for (cacheY = startY; cacheY < endY; cacheY++) {
265                 cacheX = startX;
266                 int rowBytes = stride;
267                 uint8_t* buffer = bitmapBuffer;
268 
269                 row = cacheY * cacheWidth;
270                 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
271                 while (--rowBytes >= 0) {
272                     uint8_t b = *buffer++;
273                     for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
274                         cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
275                     }
276                 }
277                 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
278 
279                 bitmapBuffer += stride;
280             }
281             break;
282         }
283         default:
284             ALOGW("Unkown glyph format: 0x%x", format);
285             break;
286     }
287 
288     row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
289     memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
290 
291     cachedGlyph->mIsValid = true;
292 }
293 
createCacheTexture(int width,int height,bool allocate)294 CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
295     CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
296 
297     if (allocate) {
298         Caches::getInstance().activeTexture(0);
299         cacheTexture->allocateTexture();
300         cacheTexture->allocateMesh();
301     }
302 
303     return cacheTexture;
304 }
305 
initTextTexture()306 void FontRenderer::initTextTexture() {
307     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
308         delete mCacheTextures[i];
309     }
310     mCacheTextures.clear();
311 
312     mUploadTexture = false;
313     mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
314     mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
315     mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
316     mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
317     mCurrentCacheTexture = mCacheTextures[0];
318 }
319 
320 // Avoid having to reallocate memory and render quad by quad
initVertexArrayBuffers()321 void FontRenderer::initVertexArrayBuffers() {
322     uint32_t numIndices = gMaxNumberOfQuads * 6;
323     uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
324     uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
325 
326     // Four verts, two triangles , six indices per quad
327     for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
328         int i6 = i * 6;
329         int i4 = i * 4;
330 
331         indexBufferData[i6 + 0] = i4 + 0;
332         indexBufferData[i6 + 1] = i4 + 1;
333         indexBufferData[i6 + 2] = i4 + 2;
334 
335         indexBufferData[i6 + 3] = i4 + 0;
336         indexBufferData[i6 + 4] = i4 + 2;
337         indexBufferData[i6 + 5] = i4 + 3;
338     }
339 
340     glGenBuffers(1, &mIndexBufferID);
341     Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
342     glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
343 
344     free(indexBufferData);
345 }
346 
347 // We don't want to allocate anything unless we actually draw text
checkInit()348 void FontRenderer::checkInit() {
349     if (mInitialized) {
350         return;
351     }
352 
353     initTextTexture();
354     initVertexArrayBuffers();
355 
356     mInitialized = true;
357 }
358 
checkTextureUpdate()359 void FontRenderer::checkTextureUpdate() {
360     if (!mUploadTexture) {
361         return;
362     }
363 
364     Caches& caches = Caches::getInstance();
365     GLuint lastTextureId = 0;
366 
367     bool resetPixelStore = false;
368     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
369 
370     // Iterate over all the cache textures and see which ones need to be updated
371     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
372         CacheTexture* cacheTexture = mCacheTextures[i];
373         if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
374             if (cacheTexture->getTextureId() != lastTextureId) {
375                 lastTextureId = cacheTexture->getTextureId();
376                 caches.activeTexture(0);
377                 glBindTexture(GL_TEXTURE_2D, lastTextureId);
378             }
379 
380             if (cacheTexture->upload()) {
381                 resetPixelStore = true;
382             }
383 
384 #if DEBUG_FONT_RENDERER
385             ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
386                     i, x, y, width, height);
387 #endif
388         }
389     }
390 
391     // Unbind any PBO we might have used to update textures
392     caches.unbindPixelBuffer();
393 
394     // Reset to default unpack row length to avoid affecting texture
395     // uploads in other parts of the renderer
396     if (resetPixelStore) {
397         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
398     }
399 
400     mUploadTexture = false;
401 }
402 
issueDrawCommand()403 void FontRenderer::issueDrawCommand() {
404     bool first = true;
405     bool force = false;
406 
407     GLuint lastId = 0;
408     Caches& caches = Caches::getInstance();
409 
410     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
411         CacheTexture* texture = mCacheTextures[i];
412         if (texture->canDraw()) {
413             if (first) {
414                 if (mFunctor) (*mFunctor)(0, NULL);
415 
416                 checkTextureUpdate();
417                 caches.bindIndicesBuffer(mIndexBufferID);
418 
419                 if (!mDrawn) {
420                     // If returns true, a VBO was bound and we must
421                     // rebind our vertex attrib pointers even if
422                     // they have the same values as the current pointers
423                     force = caches.unbindMeshBuffer();
424                 }
425 
426                 caches.activeTexture(0);
427                 first = false;
428             }
429 
430             glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
431             texture->setLinearFiltering(mLinearFiltering, false);
432 
433             TextureVertex* mesh = texture->mesh();
434             caches.bindPositionVertexPointer(force, &mesh[0].position[0]);
435             caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]);
436             force = false;
437 
438             glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
439                     GL_UNSIGNED_SHORT, texture->indices());
440 
441             texture->resetMesh();
442         }
443     }
444 
445     mDrawn = true;
446 }
447 
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)448 void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
449         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
450         float x4, float y4, float u4, float v4, CacheTexture* texture) {
451     if (texture != mCurrentCacheTexture) {
452         // Now use the new texture id
453         mCurrentCacheTexture = texture;
454     }
455 
456     mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2,
457             x3, y3, u3, v3, x4, y4, u4, v4);
458 }
459 
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)460 void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
461         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
462         float x4, float y4, float u4, float v4, CacheTexture* texture) {
463 
464     if (mClip &&
465             (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
466         return;
467     }
468 
469     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
470 
471     if (mBounds) {
472         mBounds->left = fmin(mBounds->left, x1);
473         mBounds->top = fmin(mBounds->top, y3);
474         mBounds->right = fmax(mBounds->right, x3);
475         mBounds->bottom = fmax(mBounds->bottom, y1);
476     }
477 
478     if (mCurrentCacheTexture->endOfMesh()) {
479         issueDrawCommand();
480     }
481 }
482 
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)483 void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
484         float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
485         float x4, float y4, float u4, float v4, CacheTexture* texture) {
486 
487     appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
488 
489     if (mBounds) {
490         mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
491         mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
492         mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
493         mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
494     }
495 
496     if (mCurrentCacheTexture->endOfMesh()) {
497         issueDrawCommand();
498     }
499 }
500 
setFont(SkPaint * paint,const mat4 & matrix)501 void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
502     mCurrentFont = Font::create(this, paint, matrix);
503 }
504 
renderDropShadow(SkPaint * paint,const char * text,uint32_t startIndex,uint32_t len,int numGlyphs,uint32_t radius,const float * positions)505 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
506         uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
507     checkInit();
508 
509     DropShadow image;
510     image.width = 0;
511     image.height = 0;
512     image.image = NULL;
513     image.penX = 0;
514     image.penY = 0;
515 
516     if (!mCurrentFont) {
517         return image;
518     }
519 
520     mDrawn = false;
521     mClip = NULL;
522     mBounds = NULL;
523 
524     Rect bounds;
525     mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
526 
527     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
528     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
529 
530     uint32_t maxSize = Caches::getInstance().maxTextureSize;
531     if (paddedWidth > maxSize || paddedHeight > maxSize) {
532         return image;
533     }
534 
535     // Align buffers for renderscript usage
536     if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
537         paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
538     }
539 
540     int size = paddedWidth * paddedHeight;
541     uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
542     memset(dataBuffer, 0, size);
543 
544     int penX = radius - bounds.left;
545     int penY = radius - bounds.bottom;
546 
547     if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
548         // text has non-whitespace, so draw and blur to create the shadow
549         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
550         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
551         mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
552                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
553 
554         // Unbind any PBO we might have used
555         Caches::getInstance().unbindPixelBuffer();
556 
557         blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
558     }
559 
560     image.width = paddedWidth;
561     image.height = paddedHeight;
562     image.image = dataBuffer;
563     image.penX = penX;
564     image.penY = penY;
565 
566     return image;
567 }
568 
initRender(const Rect * clip,Rect * bounds,Functor * functor)569 void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
570     checkInit();
571 
572     mDrawn = false;
573     mBounds = bounds;
574     mFunctor = functor;
575     mClip = clip;
576 }
577 
finishRender()578 void FontRenderer::finishRender() {
579     mBounds = NULL;
580     mClip = NULL;
581 
582     issueDrawCommand();
583 }
584 
precache(SkPaint * paint,const char * text,int numGlyphs,const mat4 & matrix)585 void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
586     Font* font = Font::create(this, paint, matrix);
587     font->precache(paint, text, numGlyphs);
588 }
589 
endPrecaching()590 void FontRenderer::endPrecaching() {
591     checkTextureUpdate();
592 }
593 
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)594 bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
595         uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
596         const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
597     if (!mCurrentFont) {
598         ALOGE("No font set");
599         return false;
600     }
601 
602     initRender(clip, bounds, functor);
603     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
604 
605     if (forceFinish) {
606         finishRender();
607     }
608 
609     return mDrawn;
610 }
611 
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)612 bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
613         uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
614         float hOffset, float vOffset, Rect* bounds) {
615     if (!mCurrentFont) {
616         ALOGE("No font set");
617         return false;
618     }
619 
620     initRender(clip, bounds, NULL);
621     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
622     finishRender();
623 
624     return mDrawn;
625 }
626 
removeFont(const Font * font)627 void FontRenderer::removeFont(const Font* font) {
628     mActiveFonts.remove(font->getDescription());
629 
630     if (mCurrentFont == font) {
631         mCurrentFont = NULL;
632     }
633 }
634 
blurImage(uint8_t ** image,int32_t width,int32_t height,int32_t radius)635 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
636     if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
637         float *gaussian = new float[2 * radius + 1];
638         Blur::generateGaussianWeights(gaussian, radius);
639 
640         uint8_t* scratch = new uint8_t[width * height];
641         Blur::horizontal(gaussian, radius, *image, scratch, width, height);
642         Blur::vertical(gaussian, radius, scratch, *image, width, height);
643 
644         delete[] gaussian;
645         delete[] scratch;
646         return;
647     }
648 
649     uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
650 
651     if (mRs.get() == 0) {
652         mRs = new RSC::RS();
653         if (!mRs->init(true, true)) {
654             ALOGE("blur RS failed to init");
655         }
656 
657         mRsElement = RSC::Element::A_8(mRs);
658         mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
659     }
660 
661     sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
662     sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
663             RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
664     sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
665             RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
666 
667     mRsScript->setRadius(radius);
668     mRsScript->blur(ain, aout);
669 
670     // replace the original image's pointer, avoiding a copy back to the original buffer
671     free(*image);
672     *image = outImage;
673 }
674 
getCacheSize() const675 uint32_t FontRenderer::getCacheSize() const {
676     uint32_t size = 0;
677     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
678         CacheTexture* cacheTexture = mCacheTextures[i];
679         if (cacheTexture && cacheTexture->getPixelBuffer()) {
680             size += cacheTexture->getPixelBuffer()->getSize();
681         }
682     }
683     return size;
684 }
685 
686 }; // namespace uirenderer
687 }; // namespace android
688