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