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