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