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