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 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 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 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 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 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 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 195 void FontRenderer::flushLargeCaches() { 196 flushLargeCaches(mACacheTextures); 197 flushLargeCaches(mRGBACacheTextures); 198 } 199 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 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 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 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 424 void FontRenderer::checkInit() { 425 if (mInitialized) { 426 return; 427 } 428 429 initTextTexture(); 430 431 mInitialized = true; 432 } 433 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 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 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 498 void FontRenderer::issueDrawCommand() { 499 issueDrawCommand(mACacheTextures); 500 issueDrawCommand(mRGBACacheTextures); 501 } 502 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 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 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 556 void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { 557 mCurrentFont = Font::create(this, paint, matrix); 558 } 559 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 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 639 void FontRenderer::finishRender() { 640 mBounds = nullptr; 641 mClip = nullptr; 642 643 issueDrawCommand(); 644 } 645 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 652 void FontRenderer::endPrecaching() { 653 checkTextureUpdate(); 654 } 655 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 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 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 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 750 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