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