FontRenderer.cpp revision b29b16e5423fe7deab8ebf3da512c6d2192f19b5
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 "font/Font.h" 26#include "Glop.h" 27#include "GlopBuilder.h" 28#include "PixelBuffer.h" 29#include "Rect.h" 30#include "renderstate/RenderState.h" 31#include "utils/Blur.h" 32#include "utils/Timing.h" 33 34#include <algorithm> 35#include <cutils/properties.h> 36#include <SkGlyph.h> 37#include <SkUtils.h> 38#include <utils/Log.h> 39 40#ifdef ANDROID_ENABLE_RENDERSCRIPT 41#include <RenderScript.h> 42#endif 43 44namespace android { 45namespace uirenderer { 46 47// blur inputs smaller than this constant will bypass renderscript 48#define RS_MIN_INPUT_CUTOFF 10000 49 50/////////////////////////////////////////////////////////////////////////////// 51// TextSetupFunctor 52/////////////////////////////////////////////////////////////////////////////// 53 54void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { 55 int textureFillFlags = TextureFillFlags::None; 56 if (texture.getFormat() == GL_ALPHA) { 57 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 58 } 59 if (linearFiltering) { 60 textureFillFlags |= TextureFillFlags::ForceFilter; 61 } 62 int transformFlags = pureTranslate 63 ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; 64#ifdef ANDROID_ENABLE_LINEAR_BLENDING 65 bool gammaCorrection = true; 66#else 67 bool gammaCorrection = false; 68#endif 69 Glop glop; 70 GlopBuilder(renderer->renderState(), renderer->caches(), &glop) 71 .setRoundRectClipState(bakedState->roundRectClipState) 72 .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) 73 .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) 74 .setGammaCorrection(gammaCorrection) 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} 81 82/////////////////////////////////////////////////////////////////////////////// 83// FontRenderer 84/////////////////////////////////////////////////////////////////////////////// 85 86static bool sLogFontRendererCreate = true; 87 88FontRenderer::FontRenderer(const uint8_t* gammaTable) 89 : mGammaTable(gammaTable) 90 , mCurrentFont(nullptr) 91 , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) 92 , mCurrentCacheTexture(nullptr) 93 , mUploadTexture(false) 94 , mFunctor(nullptr) 95 , mClip(nullptr) 96 , mBounds(nullptr) 97 , mDrawn(false) 98 , mInitialized(false) 99 , mLinearFiltering(false) { 100 101 if (sLogFontRendererCreate) { 102 INIT_LOGD("Creating FontRenderer"); 103 } 104 105 mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH, 106 DEFAULT_TEXT_SMALL_CACHE_WIDTH); 107 mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, 108 DEFAULT_TEXT_SMALL_CACHE_HEIGHT); 109 110 mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH, 111 DEFAULT_TEXT_LARGE_CACHE_WIDTH); 112 mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, 113 DEFAULT_TEXT_LARGE_CACHE_HEIGHT); 114 115 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 116 117 mSmallCacheWidth = std::min(mSmallCacheWidth, maxTextureSize); 118 mSmallCacheHeight = std::min(mSmallCacheHeight, maxTextureSize); 119 mLargeCacheWidth = std::min(mLargeCacheWidth, maxTextureSize); 120 mLargeCacheHeight = std::min(mLargeCacheHeight, maxTextureSize); 121 122 if (sLogFontRendererCreate) { 123 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 124 mSmallCacheWidth, mSmallCacheHeight, 125 mLargeCacheWidth, mLargeCacheHeight >> 1, 126 mLargeCacheWidth, mLargeCacheHeight >> 1, 127 mLargeCacheWidth, mLargeCacheHeight); 128 } 129 130 sLogFontRendererCreate = false; 131} 132 133void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) { 134 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 135 delete cacheTextures[i]; 136 } 137 cacheTextures.clear(); 138} 139 140FontRenderer::~FontRenderer() { 141 clearCacheTextures(mACacheTextures); 142 clearCacheTextures(mRGBACacheTextures); 143 144 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 145 while (it.next()) { 146 delete it.value(); 147 } 148 mActiveFonts.clear(); 149} 150 151void FontRenderer::flushAllAndInvalidate() { 152 issueDrawCommand(); 153 154 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 155 while (it.next()) { 156 it.value()->invalidateTextureCache(); 157 } 158 159 for (uint32_t i = 0; i < mACacheTextures.size(); i++) { 160 mACacheTextures[i]->init(); 161 162#ifdef BUGREPORT_FONT_CACHE_USAGE 163 mHistoryTracker.glyphsCleared(mACacheTextures[i]); 164#endif 165 } 166 167 for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { 168 mRGBACacheTextures[i]->init(); 169#ifdef BUGREPORT_FONT_CACHE_USAGE 170 mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]); 171#endif 172 } 173 174 mDrawn = false; 175} 176 177void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) { 178 // Start from 1; don't deallocate smallest/default texture 179 for (uint32_t i = 1; i < cacheTextures.size(); i++) { 180 CacheTexture* cacheTexture = cacheTextures[i]; 181 if (cacheTexture->getPixelBuffer()) { 182 cacheTexture->init(); 183#ifdef BUGREPORT_FONT_CACHE_USAGE 184 mHistoryTracker.glyphsCleared(cacheTexture); 185#endif 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 row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX 298 - TEXTURE_BORDER_SIZE; 299 // write leading border line 300 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 301 // write glyph data 302 if (mGammaTable) { 303 for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 304 row = cacheY * cacheWidth; 305 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 306 for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 307 uint8_t tempCol = bitmapBuffer[bY + bX]; 308 cacheBuffer[row + cacheX] = mGammaTable[tempCol]; 309 } 310 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 311 } 312 } else { 313 for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 314 row = cacheY * cacheWidth; 315 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); 316 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 317 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 318 } 319 } 320 // write trailing border line 321 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; 322 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 323 break; 324 } 325 case SkMask::kARGB32_Format: { 326 // prep data lengths 327 const size_t formatSize = PixelBuffer::formatSize(GL_RGBA); 328 const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE; 329 size_t rowSize = formatSize * glyph.fWidth; 330 // prep advances 331 size_t dstStride = formatSize * cacheWidth; 332 // prep indices 333 // - we actually start one row early, and then increment before first copy 334 uint8_t* src = &bitmapBuffer[0 - srcStride]; 335 uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)]; 336 uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)]; 337 uint8_t* dstL = dst - borderSize; 338 uint8_t* dstR = dst + rowSize; 339 // write leading border line 340 memset(dstL, 0, rowSize + 2 * borderSize); 341 // write glyph data 342 while (dst < dstEnd) { 343 memset(dstL += dstStride, 0, borderSize); // leading border column 344 memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data 345 memset(dstR += dstStride, 0, borderSize); // trailing border column 346 } 347 // write trailing border line 348 memset(dstL += dstStride, 0, rowSize + 2 * borderSize); 349 break; 350 } 351 case SkMask::kBW_Format: { 352 uint32_t cacheX = 0, cacheY = 0; 353 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX 354 - TEXTURE_BORDER_SIZE; 355 static const uint8_t COLORS[2] = { 0, 255 }; 356 // write leading border line 357 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 358 // write glyph data 359 for (cacheY = startY; cacheY < endY; cacheY++) { 360 cacheX = startX; 361 int rowBytes = srcStride; 362 uint8_t* buffer = bitmapBuffer; 363 364 row = cacheY * cacheWidth; 365 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 366 while (--rowBytes >= 0) { 367 uint8_t b = *buffer++; 368 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { 369 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; 370 } 371 } 372 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 373 374 bitmapBuffer += srcStride; 375 } 376 // write trailing border line 377 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; 378 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 379 break; 380 } 381 default: 382 ALOGW("Unknown glyph format: 0x%x", format); 383 break; 384 } 385 386 cachedGlyph->mIsValid = true; 387 388#ifdef BUGREPORT_FONT_CACHE_USAGE 389 mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight); 390#endif 391} 392 393CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, 394 bool allocate) { 395 CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads); 396 397 if (allocate) { 398 Caches::getInstance().textureState().activateTexture(0); 399 cacheTexture->allocatePixelBuffer(); 400 cacheTexture->allocateMesh(); 401 } 402 403 return cacheTexture; 404} 405 406void FontRenderer::initTextTexture() { 407 clearCacheTextures(mACacheTextures); 408 clearCacheTextures(mRGBACacheTextures); 409 410 mUploadTexture = false; 411 mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 412 GL_ALPHA, true)); 413 mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 414 GL_ALPHA, false)); 415 mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 416 GL_ALPHA, false)); 417 mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, 418 GL_ALPHA, false)); 419 mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 420 GL_RGBA, false)); 421 mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 422 GL_RGBA, false)); 423 mCurrentCacheTexture = mACacheTextures[0]; 424} 425 426// We don't want to allocate anything unless we actually draw text 427void FontRenderer::checkInit() { 428 if (mInitialized) { 429 return; 430 } 431 432 initTextTexture(); 433 434 mInitialized = true; 435} 436 437void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures, 438 bool& resetPixelStore, GLuint& lastTextureId) { 439 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 440 CacheTexture* cacheTexture = cacheTextures[i]; 441 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { 442 if (cacheTexture->getTextureId() != lastTextureId) { 443 lastTextureId = cacheTexture->getTextureId(); 444 caches.textureState().activateTexture(0); 445 caches.textureState().bindTexture(lastTextureId); 446 } 447 448 if (cacheTexture->upload()) { 449 resetPixelStore = true; 450 } 451 } 452 } 453} 454 455void FontRenderer::checkTextureUpdate() { 456 if (!mUploadTexture) { 457 return; 458 } 459 460 Caches& caches = Caches::getInstance(); 461 GLuint lastTextureId = 0; 462 463 bool resetPixelStore = false; 464 465 // Iterate over all the cache textures and see which ones need to be updated 466 checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId); 467 checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId); 468 469 // Unbind any PBO we might have used to update textures 470 caches.pixelBufferState().unbind(); 471 472 // Reset to default unpack row length to avoid affecting texture 473 // uploads in other parts of the renderer 474 if (resetPixelStore) { 475 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 476 } 477 478 mUploadTexture = false; 479} 480 481void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) { 482 if (!mFunctor) return; 483 484 bool first = true; 485 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 486 CacheTexture* texture = cacheTextures[i]; 487 if (texture->canDraw()) { 488 if (first) { 489 checkTextureUpdate(); 490 first = false; 491 mDrawn = true; 492 } 493 494 mFunctor->draw(*texture, mLinearFiltering); 495 496 texture->resetMesh(); 497 } 498 } 499} 500 501void FontRenderer::issueDrawCommand() { 502 issueDrawCommand(mACacheTextures); 503 issueDrawCommand(mRGBACacheTextures); 504} 505 506void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 507 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 508 float x4, float y4, float u4, float v4, CacheTexture* texture) { 509 if (texture != mCurrentCacheTexture) { 510 // Now use the new texture id 511 mCurrentCacheTexture = texture; 512 } 513 514 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, 515 x3, y3, u3, v3, x4, y4, u4, v4); 516} 517 518void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 519 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 520 float x4, float y4, float u4, float v4, CacheTexture* texture) { 521 522 if (mClip && 523 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 524 return; 525 } 526 527 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 528 529 if (mBounds) { 530 mBounds->left = std::min(mBounds->left, x1); 531 mBounds->top = std::min(mBounds->top, y3); 532 mBounds->right = std::max(mBounds->right, x3); 533 mBounds->bottom = std::max(mBounds->bottom, y1); 534 } 535 536 if (mCurrentCacheTexture->endOfMesh()) { 537 issueDrawCommand(); 538 } 539} 540 541void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 542 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 543 float x4, float y4, float u4, float v4, CacheTexture* texture) { 544 545 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 546 547 if (mBounds) { 548 mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4)))); 549 mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4)))); 550 mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4)))); 551 mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4)))); 552 } 553 554 if (mCurrentCacheTexture->endOfMesh()) { 555 issueDrawCommand(); 556 } 557} 558 559void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { 560 mCurrentFont = Font::create(this, paint, matrix); 561} 562 563FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, 564 int numGlyphs, float radius, const float* positions) { 565 checkInit(); 566 567 DropShadow image; 568 image.width = 0; 569 image.height = 0; 570 image.image = nullptr; 571 image.penX = 0; 572 image.penY = 0; 573 574 if (!mCurrentFont) { 575 return image; 576 } 577 578 mDrawn = false; 579 mClip = nullptr; 580 mBounds = nullptr; 581 582 Rect bounds; 583 mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions); 584 585 uint32_t intRadius = Blur::convertRadiusToInt(radius); 586 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; 587 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; 588 589 uint32_t maxSize = Caches::getInstance().maxTextureSize; 590 if (paddedWidth > maxSize || paddedHeight > maxSize) { 591 return image; 592 } 593 594#ifdef ANDROID_ENABLE_RENDERSCRIPT 595 // Align buffers for renderscript usage 596 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 597 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 598 } 599 int size = paddedWidth * paddedHeight; 600 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 601#else 602 int size = paddedWidth * paddedHeight; 603 uint8_t* dataBuffer = (uint8_t*) malloc(size); 604#endif 605 606 memset(dataBuffer, 0, size); 607 608 int penX = intRadius - bounds.left; 609 int penY = intRadius - bounds.bottom; 610 611 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 612 // text has non-whitespace, so draw and blur to create the shadow 613 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 614 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 615 mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, 616 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); 617 618 // Unbind any PBO we might have used 619 Caches::getInstance().pixelBufferState().unbind(); 620 621 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 622 } 623 624 image.width = paddedWidth; 625 image.height = paddedHeight; 626 image.image = dataBuffer; 627 image.penX = penX; 628 image.penY = penY; 629 630 return image; 631} 632 633void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) { 634 checkInit(); 635 636 mDrawn = false; 637 mBounds = bounds; 638 mFunctor = functor; 639 mClip = clip; 640} 641 642void FontRenderer::finishRender() { 643 mBounds = nullptr; 644 mClip = nullptr; 645 646 issueDrawCommand(); 647} 648 649void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, 650 const SkMatrix& matrix) { 651 Font* font = Font::create(this, paint, matrix); 652 font->precache(paint, glyphs, numGlyphs); 653} 654 655void FontRenderer::endPrecaching() { 656 checkTextureUpdate(); 657} 658 659bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, 660 int numGlyphs, int x, int y, const float* positions, 661 Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { 662 if (!mCurrentFont) { 663 ALOGE("No font set"); 664 return false; 665 } 666 667 initRender(clip, bounds, functor); 668 mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions); 669 670 if (forceFinish) { 671 finishRender(); 672 } 673 674 return mDrawn; 675} 676 677bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, 678 int numGlyphs, const SkPath* path, float hOffset, float vOffset, 679 Rect* bounds, TextDrawFunctor* functor) { 680 if (!mCurrentFont) { 681 ALOGE("No font set"); 682 return false; 683 } 684 685 initRender(clip, bounds, functor); 686 mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset); 687 finishRender(); 688 689 return mDrawn; 690} 691 692void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { 693 uint32_t intRadius = Blur::convertRadiusToInt(radius); 694#ifdef ANDROID_ENABLE_RENDERSCRIPT 695 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) { 696 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 697 698 if (mRs == nullptr) { 699 mRs = new RSC::RS(); 700 // a null path is OK because there are no custom kernels used 701 // hence nothing gets cached by RS 702 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { 703 mRs.clear(); 704 ALOGE("blur RS failed to init"); 705 } else { 706 mRsElement = RSC::Element::A_8(mRs); 707 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); 708 } 709 } 710 if (mRs != nullptr) { 711 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 712 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, 713 RS_ALLOCATION_MIPMAP_NONE, 714 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 715 *image); 716 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, 717 RS_ALLOCATION_MIPMAP_NONE, 718 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 719 outImage); 720 721 mRsScript->setRadius(radius); 722 mRsScript->setInput(ain); 723 mRsScript->forEach(aout); 724 725 // replace the original image's pointer, avoiding a copy back to the original buffer 726 free(*image); 727 *image = outImage; 728 729 return; 730 } 731 } 732#endif 733 734 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); 735 Blur::generateGaussianWeights(gaussian.get(), radius); 736 737 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); 738 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); 739 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); 740} 741 742static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) { 743 uint32_t size = 0; 744 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 745 CacheTexture* cacheTexture = cacheTextures[i]; 746 if (cacheTexture && cacheTexture->getPixelBuffer()) { 747 size += cacheTexture->getPixelBuffer()->getSize(); 748 } 749 } 750 return size; 751} 752 753static uint32_t calculateFreeCacheSize(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->calculateFreeMemory(); 759 } 760 } 761 return size; 762} 763 764const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const { 765 switch (format) { 766 case GL_ALPHA: { 767 return mACacheTextures; 768 } 769 case GL_RGBA: { 770 return mRGBACacheTextures; 771 } 772 default: { 773 LOG_ALWAYS_FATAL("Unsupported format: %d", format); 774 // Impossible to hit this, but the compiler doesn't know that 775 return *(new std::vector<CacheTexture*>()); 776 } 777 } 778} 779 780static void dumpTextures(String8& log, const char* tag, 781 const std::vector<CacheTexture*>& cacheTextures) { 782 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 783 CacheTexture* cacheTexture = cacheTextures[i]; 784 if (cacheTexture && cacheTexture->getPixelBuffer()) { 785 uint32_t free = cacheTexture->calculateFreeMemory(); 786 uint32_t total = cacheTexture->getPixelBuffer()->getSize(); 787 log.appendFormat(" %-4s texture %d %8d / %8d\n", tag, i, total - free, total); 788 } 789 } 790} 791 792void FontRenderer::dumpMemoryUsage(String8& log) const { 793 const uint32_t sizeA8 = getCacheSize(GL_ALPHA); 794 const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA); 795 const uint32_t sizeRGBA = getCacheSize(GL_RGBA); 796 const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA); 797 log.appendFormat(" FontRenderer A8 %8d / %8d\n", usedA8, sizeA8); 798 dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA)); 799 log.appendFormat(" FontRenderer RGBA %8d / %8d\n", usedRGBA, sizeRGBA); 800 dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA)); 801 log.appendFormat(" FontRenderer total %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA); 802} 803 804uint32_t FontRenderer::getCacheSize(GLenum format) const { 805 return calculateCacheSize(cacheTexturesForFormat(format)); 806} 807 808uint32_t FontRenderer::getFreeCacheSize(GLenum format) const { 809 return calculateFreeCacheSize(cacheTexturesForFormat(format)); 810} 811 812uint32_t FontRenderer::getSize() const { 813 return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA); 814} 815 816}; // namespace uirenderer 817}; // namespace android 818