FontRenderer.cpp revision 15c3f19a445b8df575911a16e8a6dba755a084b5
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 49namespace android { 50namespace 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 59void 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 .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) 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(0, 0, 0, 0)) 87 .build(); 88 renderer->renderGlop(glop); 89#endif 90} 91 92/////////////////////////////////////////////////////////////////////////////// 93// FontRenderer 94/////////////////////////////////////////////////////////////////////////////// 95 96static bool sLogFontRendererCreate = true; 97 98FontRenderer::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 143void 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 150FontRenderer::~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 161void 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 180void 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 195void FontRenderer::flushLargeCaches() { 196 flushLargeCaches(mACacheTextures); 197 flushLargeCaches(mRGBACacheTextures); 198} 199 200CacheTexture* 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 211void 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 390CacheTexture* 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 403void 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 424void FontRenderer::checkInit() { 425 if (mInitialized) { 426 return; 427 } 428 429 initTextTexture(); 430 431 mInitialized = true; 432} 433 434void 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 452void FontRenderer::checkTextureUpdate() { 453 if (!mUploadTexture) { 454 return; 455 } 456 457 Caches& caches = Caches::getInstance(); 458 GLuint lastTextureId = 0; 459 460 bool resetPixelStore = false; 461 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 462 463 // Iterate over all the cache textures and see which ones need to be updated 464 checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId); 465 checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId); 466 467 // Unbind any PBO we might have used to update textures 468 caches.pixelBufferState().unbind(); 469 470 // Reset to default unpack row length to avoid affecting texture 471 // uploads in other parts of the renderer 472 if (resetPixelStore) { 473 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 474 } 475 476 mUploadTexture = false; 477} 478 479void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) { 480 if (!mFunctor) return; 481 482 bool first = true; 483 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 484 CacheTexture* texture = cacheTextures[i]; 485 if (texture->canDraw()) { 486 if (first) { 487 checkTextureUpdate(); 488 first = false; 489 mDrawn = true; 490 } 491 492 mFunctor->draw(*texture, mLinearFiltering); 493 494 texture->resetMesh(); 495 } 496 } 497} 498 499void FontRenderer::issueDrawCommand() { 500 issueDrawCommand(mACacheTextures); 501 issueDrawCommand(mRGBACacheTextures); 502} 503 504void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 505 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 506 float x4, float y4, float u4, float v4, CacheTexture* texture) { 507 if (texture != mCurrentCacheTexture) { 508 // Now use the new texture id 509 mCurrentCacheTexture = texture; 510 } 511 512 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, 513 x3, y3, u3, v3, x4, y4, u4, v4); 514} 515 516void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 517 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 518 float x4, float y4, float u4, float v4, CacheTexture* texture) { 519 520 if (mClip && 521 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 522 return; 523 } 524 525 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 526 527 if (mBounds) { 528 mBounds->left = std::min(mBounds->left, x1); 529 mBounds->top = std::min(mBounds->top, y3); 530 mBounds->right = std::max(mBounds->right, x3); 531 mBounds->bottom = std::max(mBounds->bottom, y1); 532 } 533 534 if (mCurrentCacheTexture->endOfMesh()) { 535 issueDrawCommand(); 536 } 537} 538 539void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 540 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 541 float x4, float y4, float u4, float v4, CacheTexture* texture) { 542 543 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 544 545 if (mBounds) { 546 mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4)))); 547 mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4)))); 548 mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4)))); 549 mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4)))); 550 } 551 552 if (mCurrentCacheTexture->endOfMesh()) { 553 issueDrawCommand(); 554 } 555} 556 557void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { 558 mCurrentFont = Font::create(this, paint, matrix); 559} 560 561FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, 562 int numGlyphs, float radius, const float* positions) { 563 checkInit(); 564 565 DropShadow image; 566 image.width = 0; 567 image.height = 0; 568 image.image = nullptr; 569 image.penX = 0; 570 image.penY = 0; 571 572 if (!mCurrentFont) { 573 return image; 574 } 575 576 mDrawn = false; 577 mClip = nullptr; 578 mBounds = nullptr; 579 580 Rect bounds; 581 mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions); 582 583 uint32_t intRadius = Blur::convertRadiusToInt(radius); 584 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; 585 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; 586 587 uint32_t maxSize = Caches::getInstance().maxTextureSize; 588 if (paddedWidth > maxSize || paddedHeight > maxSize) { 589 return image; 590 } 591 592#ifdef ANDROID_ENABLE_RENDERSCRIPT 593 // Align buffers for renderscript usage 594 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 595 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 596 } 597 int size = paddedWidth * paddedHeight; 598 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 599#else 600 int size = paddedWidth * paddedHeight; 601 uint8_t* dataBuffer = (uint8_t*) malloc(size); 602#endif 603 604 memset(dataBuffer, 0, size); 605 606 int penX = intRadius - bounds.left; 607 int penY = intRadius - bounds.bottom; 608 609 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 610 // text has non-whitespace, so draw and blur to create the shadow 611 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 612 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 613 mCurrentFont->render(paint, text, numGlyphs, penX, penY, 614 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); 615 616 // Unbind any PBO we might have used 617 Caches::getInstance().pixelBufferState().unbind(); 618 619 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 620 } 621 622 image.width = paddedWidth; 623 image.height = paddedHeight; 624 image.image = dataBuffer; 625 image.penX = penX; 626 image.penY = penY; 627 628 return image; 629} 630 631void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) { 632 checkInit(); 633 634 mDrawn = false; 635 mBounds = bounds; 636 mFunctor = functor; 637 mClip = clip; 638} 639 640void FontRenderer::finishRender() { 641 mBounds = nullptr; 642 mClip = nullptr; 643 644 issueDrawCommand(); 645} 646 647void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, 648 const SkMatrix& matrix) { 649 Font* font = Font::create(this, paint, matrix); 650 font->precache(paint, text, numGlyphs); 651} 652 653void FontRenderer::endPrecaching() { 654 checkTextureUpdate(); 655} 656 657bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, 658 int numGlyphs, int x, int y, const float* positions, 659 Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { 660 if (!mCurrentFont) { 661 ALOGE("No font set"); 662 return false; 663 } 664 665 initRender(clip, bounds, functor); 666 mCurrentFont->render(paint, text, numGlyphs, x, y, positions); 667 668 if (forceFinish) { 669 finishRender(); 670 } 671 672 return mDrawn; 673} 674 675bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, 676 int numGlyphs, const SkPath* path, float hOffset, float vOffset, 677 Rect* bounds, TextDrawFunctor* functor) { 678 if (!mCurrentFont) { 679 ALOGE("No font set"); 680 return false; 681 } 682 683 initRender(clip, bounds, functor); 684 mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset); 685 finishRender(); 686 687 return mDrawn; 688} 689 690void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { 691 uint32_t intRadius = Blur::convertRadiusToInt(radius); 692#ifdef ANDROID_ENABLE_RENDERSCRIPT 693 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { 694 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 695 696 if (mRs == nullptr) { 697 mRs = new RSC::RS(); 698 // a null path is OK because there are no custom kernels used 699 // hence nothing gets cached by RS 700 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { 701 mRs.clear(); 702 ALOGE("blur RS failed to init"); 703 } else { 704 mRsElement = RSC::Element::A_8(mRs); 705 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); 706 } 707 } 708 if (mRs != nullptr) { 709 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 710 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, 711 RS_ALLOCATION_MIPMAP_NONE, 712 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 713 *image); 714 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, 715 RS_ALLOCATION_MIPMAP_NONE, 716 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 717 outImage); 718 719 mRsScript->setRadius(radius); 720 mRsScript->setInput(ain); 721 mRsScript->forEach(aout); 722 723 // replace the original image's pointer, avoiding a copy back to the original buffer 724 free(*image); 725 *image = outImage; 726 727 return; 728 } 729 } 730#endif 731 732 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); 733 Blur::generateGaussianWeights(gaussian.get(), radius); 734 735 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); 736 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); 737 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); 738} 739 740static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) { 741 uint32_t size = 0; 742 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 743 CacheTexture* cacheTexture = cacheTextures[i]; 744 if (cacheTexture && cacheTexture->getPixelBuffer()) { 745 size += cacheTexture->getPixelBuffer()->getSize(); 746 } 747 } 748 return size; 749} 750 751uint32_t FontRenderer::getCacheSize(GLenum format) const { 752 switch (format) { 753 case GL_ALPHA: { 754 return calculateCacheSize(mACacheTextures); 755 } 756 case GL_RGBA: { 757 return calculateCacheSize(mRGBACacheTextures); 758 } 759 default: { 760 return 0; 761 } 762 } 763} 764 765}; // namespace uirenderer 766}; // namespace android 767