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
173 for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
174 mRGBACacheTextures[i]->init();
175 }
176
177 mDrawn = false;
178 }
179
flushLargeCaches(std::vector<CacheTexture * > & cacheTextures)180 void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) {
181 // Start from 1; don't deallocate smallest/default texture
182 for (uint32_t i = 1; i < cacheTextures.size(); i++) {
183 CacheTexture* cacheTexture = cacheTextures[i];
184 if (cacheTexture->getPixelBuffer()) {
185 cacheTexture->init();
186 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
187 while (it.next()) {
188 it.value()->invalidateTextureCache(cacheTexture);
189 }
190 cacheTexture->releasePixelBuffer();
191 }
192 }
193 }
194
flushLargeCaches()195 void FontRenderer::flushLargeCaches() {
196 flushLargeCaches(mACacheTextures);
197 flushLargeCaches(mRGBACacheTextures);
198 }
199
cacheBitmapInTexture(std::vector<CacheTexture * > & cacheTextures,const SkGlyph & glyph,uint32_t * startX,uint32_t * startY)200 CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures,
201 const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
202 for (uint32_t i = 0; i < cacheTextures.size(); i++) {
203 if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
204 return cacheTextures[i];
205 }
206 }
207 // Could not fit glyph into current cache textures
208 return nullptr;
209 }
210
cacheBitmap(const SkGlyph & glyph,CachedGlyphInfo * cachedGlyph,uint32_t * retOriginX,uint32_t * retOriginY,bool precaching)211 void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
212 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
213 checkInit();
214
215 // If the glyph bitmap is empty let's assum the glyph is valid
216 // so we can avoid doing extra work later on
217 if (glyph.fWidth == 0 || glyph.fHeight == 0) {
218 cachedGlyph->mIsValid = true;
219 cachedGlyph->mCacheTexture = nullptr;
220 return;
221 }
222
223 cachedGlyph->mIsValid = false;
224
225 // choose an appropriate cache texture list for this glyph format
226 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
227 std::vector<CacheTexture*>* cacheTextures = nullptr;
228 switch (format) {
229 case SkMask::kA8_Format:
230 case SkMask::kBW_Format:
231 cacheTextures = &mACacheTextures;
232 break;
233 case SkMask::kARGB32_Format:
234 cacheTextures = &mRGBACacheTextures;
235 break;
236 default:
237 #if DEBUG_FONT_RENDERER
238 ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
239 #endif
240 return;
241 }
242
243 // If the glyph is too tall, don't cache it
244 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
245 (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
246 ALOGE("Font size too large to fit in cache. width, height = %i, %i",
247 (int) glyph.fWidth, (int) glyph.fHeight);
248 return;
249 }
250
251 // Now copy the bitmap into the cache texture
252 uint32_t startX = 0;
253 uint32_t startY = 0;
254
255 CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
256
257 if (!cacheTexture) {
258 if (!precaching) {
259 // If the new glyph didn't fit and we are not just trying to precache it,
260 // clear out the cache and try again
261 flushAllAndInvalidate();
262 cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
263 }
264
265 if (!cacheTexture) {
266 // either the glyph didn't fit or we're precaching and will cache it when we draw
267 return;
268 }
269 }
270
271 cachedGlyph->mCacheTexture = cacheTexture;
272
273 *retOriginX = startX;
274 *retOriginY = startY;
275
276 uint32_t endX = startX + glyph.fWidth;
277 uint32_t endY = startY + glyph.fHeight;
278
279 uint32_t cacheWidth = cacheTexture->getWidth();
280
281 if (!cacheTexture->getPixelBuffer()) {
282 Caches::getInstance().textureState().activateTexture(0);
283 // Large-glyph texture memory is allocated only as needed
284 cacheTexture->allocatePixelBuffer();
285 }
286 if (!cacheTexture->mesh()) {
287 cacheTexture->allocateMesh();
288 }
289
290 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
291 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
292 int srcStride = glyph.rowBytes();
293
294 // Copy the glyph image, taking the mask format into account
295 switch (format) {
296 case SkMask::kA8_Format: {
297 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
298 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
299 - TEXTURE_BORDER_SIZE;
300 // write leading border line
301 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
302 // write glyph data
303 if (mGammaTable) {
304 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
305 row = cacheY * cacheWidth;
306 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
307 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
308 uint8_t tempCol = bitmapBuffer[bY + bX];
309 cacheBuffer[row + cacheX] = mGammaTable[tempCol];
310 }
311 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
312 }
313 } else {
314 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
315 row = cacheY * cacheWidth;
316 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
317 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
318 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
319 }
320 }
321 // write trailing border line
322 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
323 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
324 break;
325 }
326 case SkMask::kARGB32_Format: {
327 // prep data lengths
328 const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
329 const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
330 size_t rowSize = formatSize * glyph.fWidth;
331 // prep advances
332 size_t dstStride = formatSize * cacheWidth;
333 // prep indices
334 // - we actually start one row early, and then increment before first copy
335 uint8_t* src = &bitmapBuffer[0 - srcStride];
336 uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
337 uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
338 uint8_t* dstL = dst - borderSize;
339 uint8_t* dstR = dst + rowSize;
340 // write leading border line
341 memset(dstL, 0, rowSize + 2 * borderSize);
342 // write glyph data
343 while (dst < dstEnd) {
344 memset(dstL += dstStride, 0, borderSize); // leading border column
345 memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
346 memset(dstR += dstStride, 0, borderSize); // trailing border column
347 }
348 // write trailing border line
349 memset(dstL += dstStride, 0, rowSize + 2 * borderSize);
350 break;
351 }
352 case SkMask::kBW_Format: {
353 uint32_t cacheX = 0, cacheY = 0;
354 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
355 - TEXTURE_BORDER_SIZE;
356 static const uint8_t COLORS[2] = { 0, 255 };
357 // write leading border line
358 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
359 // write glyph data
360 for (cacheY = startY; cacheY < endY; cacheY++) {
361 cacheX = startX;
362 int rowBytes = srcStride;
363 uint8_t* buffer = bitmapBuffer;
364
365 row = cacheY * cacheWidth;
366 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
367 while (--rowBytes >= 0) {
368 uint8_t b = *buffer++;
369 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
370 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
371 }
372 }
373 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
374
375 bitmapBuffer += srcStride;
376 }
377 // write trailing border line
378 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
379 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
380 break;
381 }
382 default:
383 ALOGW("Unknown glyph format: 0x%x", format);
384 break;
385 }
386
387 cachedGlyph->mIsValid = true;
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 #ifdef ANDROID_ENABLE_RENDERSCRIPT
592 // Align buffers for renderscript usage
593 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
594 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
595 }
596 int size = paddedWidth * paddedHeight;
597 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
598 #else
599 int size = paddedWidth * paddedHeight;
600 uint8_t* dataBuffer = (uint8_t*) malloc(size);
601 #endif
602
603 memset(dataBuffer, 0, size);
604
605 int penX = intRadius - bounds.left;
606 int penY = intRadius - bounds.bottom;
607
608 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
609 // text has non-whitespace, so draw and blur to create the shadow
610 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
611 // TODO: don't draw pure whitespace in the first place, and avoid needing this check
612 mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
613 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
614
615 // Unbind any PBO we might have used
616 Caches::getInstance().pixelBufferState().unbind();
617
618 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
619 }
620
621 image.width = paddedWidth;
622 image.height = paddedHeight;
623 image.image = dataBuffer;
624 image.penX = penX;
625 image.penY = penY;
626
627 return image;
628 }
629
initRender(const Rect * clip,Rect * bounds,TextDrawFunctor * functor)630 void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
631 checkInit();
632
633 mDrawn = false;
634 mBounds = bounds;
635 mFunctor = functor;
636 mClip = clip;
637 }
638
finishRender()639 void FontRenderer::finishRender() {
640 mBounds = nullptr;
641 mClip = nullptr;
642
643 issueDrawCommand();
644 }
645
precache(const SkPaint * paint,const glyph_t * glyphs,int numGlyphs,const SkMatrix & matrix)646 void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
647 const SkMatrix& matrix) {
648 Font* font = Font::create(this, paint, matrix);
649 font->precache(paint, glyphs, numGlyphs);
650 }
651
endPrecaching()652 void FontRenderer::endPrecaching() {
653 checkTextureUpdate();
654 }
655
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)656 bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
657 int numGlyphs, int x, int y, const float* positions,
658 Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
659 if (!mCurrentFont) {
660 ALOGE("No font set");
661 return false;
662 }
663
664 initRender(clip, bounds, functor);
665 mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
666
667 if (forceFinish) {
668 finishRender();
669 }
670
671 return mDrawn;
672 }
673
renderTextOnPath(const SkPaint * paint,const Rect * clip,const glyph_t * glyphs,int numGlyphs,const SkPath * path,float hOffset,float vOffset,Rect * bounds,TextDrawFunctor * functor)674 bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
675 int numGlyphs, const SkPath* path, float hOffset, float vOffset,
676 Rect* bounds, TextDrawFunctor* functor) {
677 if (!mCurrentFont) {
678 ALOGE("No font set");
679 return false;
680 }
681
682 initRender(clip, bounds, functor);
683 mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
684 finishRender();
685
686 return mDrawn;
687 }
688
blurImage(uint8_t ** image,int32_t width,int32_t height,float radius)689 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
690 uint32_t intRadius = Blur::convertRadiusToInt(radius);
691 #ifdef ANDROID_ENABLE_RENDERSCRIPT
692 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
693 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
694
695 if (mRs == nullptr) {
696 mRs = new RSC::RS();
697 // a null path is OK because there are no custom kernels used
698 // hence nothing gets cached by RS
699 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
700 mRs.clear();
701 ALOGE("blur RS failed to init");
702 } else {
703 mRsElement = RSC::Element::A_8(mRs);
704 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
705 }
706 }
707 if (mRs != nullptr) {
708 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
709 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
710 RS_ALLOCATION_MIPMAP_NONE,
711 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
712 *image);
713 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t,
714 RS_ALLOCATION_MIPMAP_NONE,
715 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
716 outImage);
717
718 mRsScript->setRadius(radius);
719 mRsScript->setInput(ain);
720 mRsScript->forEach(aout);
721
722 // replace the original image's pointer, avoiding a copy back to the original buffer
723 free(*image);
724 *image = outImage;
725
726 return;
727 }
728 }
729 #endif
730
731 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
732 Blur::generateGaussianWeights(gaussian.get(), radius);
733
734 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
735 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
736 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
737 }
738
calculateCacheSize(const std::vector<CacheTexture * > & cacheTextures)739 static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
740 uint32_t size = 0;
741 for (uint32_t i = 0; i < cacheTextures.size(); i++) {
742 CacheTexture* cacheTexture = cacheTextures[i];
743 if (cacheTexture && cacheTexture->getPixelBuffer()) {
744 size += cacheTexture->getPixelBuffer()->getSize();
745 }
746 }
747 return size;
748 }
749
getCacheSize(GLenum format) const750 uint32_t FontRenderer::getCacheSize(GLenum format) const {
751 switch (format) {
752 case GL_ALPHA: {
753 return calculateCacheSize(mACacheTextures);
754 }
755 case GL_RGBA: {
756 return calculateCacheSize(mRGBACacheTextures);
757 }
758 default: {
759 return 0;
760 }
761 }
762 }
763
764 }; // namespace uirenderer
765 }; // namespace android
766