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