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