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