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