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