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