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