FontRenderer.cpp revision 9e7fcfda28fde747ba4e026772007cea77374e16
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "FontRenderer.h" 18 19#include "Caches.h" 20#include "Debug.h" 21#include "Extensions.h" 22#include "Glop.h" 23#include "GlopBuilder.h" 24#include "PixelBuffer.h" 25#include "Rect.h" 26#include "renderstate/RenderState.h" 27#include "utils/Blur.h" 28#include "utils/Timing.h" 29 30 31#if HWUI_NEW_OPS 32#include "BakedOpDispatcher.h" 33#include "BakedOpRenderer.h" 34#include "BakedOpState.h" 35#else 36#include "OpenGLRenderer.h" 37#endif 38 39#include <algorithm> 40#include <cutils/properties.h> 41#include <SkGlyph.h> 42#include <SkUtils.h> 43#include <utils/Log.h> 44 45#ifdef ANDROID_ENABLE_RENDERSCRIPT 46#include <RenderScript.h> 47#endif 48 49namespace android { 50namespace uirenderer { 51 52// blur inputs smaller than this constant will bypass renderscript 53#define RS_MIN_INPUT_CUTOFF 10000 54 55/////////////////////////////////////////////////////////////////////////////// 56// TextSetupFunctor 57/////////////////////////////////////////////////////////////////////////////// 58 59void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { 60 int textureFillFlags = TextureFillFlags::None; 61 if (texture.getFormat() == GL_ALPHA) { 62 textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; 63 } 64 if (linearFiltering) { 65 textureFillFlags |= TextureFillFlags::ForceFilter; 66 } 67 int transformFlags = pureTranslate 68 ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; 69 Glop glop; 70#if HWUI_NEW_OPS 71 GlopBuilder(renderer->renderState(), renderer->caches(), &glop) 72 .setRoundRectClipState(bakedState->roundRectClipState) 73 .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) 74 .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) 75 .setTransform(bakedState->computedState.transform, transformFlags) 76 .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) 77 .build(); 78 renderer->renderGlop(*bakedState, glop); 79#else 80 GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop) 81 .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState) 82 .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) 83 .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha) 84 .setTransform(*(renderer->currentSnapshot()), transformFlags) 85 .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) 86 .build(); 87 renderer->renderGlop(glop); 88#endif 89} 90 91/////////////////////////////////////////////////////////////////////////////// 92// FontRenderer 93/////////////////////////////////////////////////////////////////////////////// 94 95static bool sLogFontRendererCreate = true; 96 97FontRenderer::FontRenderer(const uint8_t* gammaTable) 98 : mGammaTable(gammaTable) 99 , mCurrentFont(nullptr) 100 , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) 101 , mCurrentCacheTexture(nullptr) 102 , mUploadTexture(false) 103 , mFunctor(nullptr) 104 , mClip(nullptr) 105 , mBounds(nullptr) 106 , mDrawn(false) 107 , mInitialized(false) 108 , mLinearFiltering(false) { 109 110 if (sLogFontRendererCreate) { 111 INIT_LOGD("Creating FontRenderer"); 112 } 113 114 mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH, 115 DEFAULT_TEXT_SMALL_CACHE_WIDTH); 116 mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, 117 DEFAULT_TEXT_SMALL_CACHE_HEIGHT); 118 119 mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH, 120 DEFAULT_TEXT_LARGE_CACHE_WIDTH); 121 mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, 122 DEFAULT_TEXT_LARGE_CACHE_HEIGHT); 123 124 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 125 126 mSmallCacheWidth = std::min(mSmallCacheWidth, maxTextureSize); 127 mSmallCacheHeight = std::min(mSmallCacheHeight, maxTextureSize); 128 mLargeCacheWidth = std::min(mLargeCacheWidth, maxTextureSize); 129 mLargeCacheHeight = std::min(mLargeCacheHeight, maxTextureSize); 130 131 if (sLogFontRendererCreate) { 132 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 133 mSmallCacheWidth, mSmallCacheHeight, 134 mLargeCacheWidth, mLargeCacheHeight >> 1, 135 mLargeCacheWidth, mLargeCacheHeight >> 1, 136 mLargeCacheWidth, mLargeCacheHeight); 137 } 138 139 sLogFontRendererCreate = false; 140} 141 142void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) { 143 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 144 delete cacheTextures[i]; 145 } 146 cacheTextures.clear(); 147} 148 149FontRenderer::~FontRenderer() { 150 clearCacheTextures(mACacheTextures); 151 clearCacheTextures(mRGBACacheTextures); 152 153 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 154 while (it.next()) { 155 delete it.value(); 156 } 157 mActiveFonts.clear(); 158} 159 160void FontRenderer::flushAllAndInvalidate() { 161 issueDrawCommand(); 162 163 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 164 while (it.next()) { 165 it.value()->invalidateTextureCache(); 166 } 167 168 for (uint32_t i = 0; i < mACacheTextures.size(); i++) { 169 mACacheTextures[i]->init(); 170 } 171 172 for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { 173 mRGBACacheTextures[i]->init(); 174 } 175 176 mDrawn = false; 177} 178 179void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) { 180 // Start from 1; don't deallocate smallest/default texture 181 for (uint32_t i = 1; i < cacheTextures.size(); i++) { 182 CacheTexture* cacheTexture = cacheTextures[i]; 183 if (cacheTexture->getPixelBuffer()) { 184 cacheTexture->init(); 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 cacheX = 0, bX = 0, cacheY = 0, bY = 0; 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 (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 304 row = cacheY * cacheWidth; 305 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 306 for (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 (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 389CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, 390 bool allocate) { 391 CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads); 392 393 if (allocate) { 394 Caches::getInstance().textureState().activateTexture(0); 395 cacheTexture->allocatePixelBuffer(); 396 cacheTexture->allocateMesh(); 397 } 398 399 return cacheTexture; 400} 401 402void FontRenderer::initTextTexture() { 403 clearCacheTextures(mACacheTextures); 404 clearCacheTextures(mRGBACacheTextures); 405 406 mUploadTexture = false; 407 mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 408 GL_ALPHA, true)); 409 mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 410 GL_ALPHA, false)); 411 mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 412 GL_ALPHA, false)); 413 mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, 414 GL_ALPHA, false)); 415 mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 416 GL_RGBA, false)); 417 mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 418 GL_RGBA, false)); 419 mCurrentCacheTexture = mACacheTextures[0]; 420} 421 422// We don't want to allocate anything unless we actually draw text 423void FontRenderer::checkInit() { 424 if (mInitialized) { 425 return; 426 } 427 428 initTextTexture(); 429 430 mInitialized = true; 431} 432 433void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures, 434 bool& resetPixelStore, GLuint& lastTextureId) { 435 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 436 CacheTexture* cacheTexture = cacheTextures[i]; 437 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { 438 if (cacheTexture->getTextureId() != lastTextureId) { 439 lastTextureId = cacheTexture->getTextureId(); 440 caches.textureState().activateTexture(0); 441 caches.textureState().bindTexture(lastTextureId); 442 } 443 444 if (cacheTexture->upload()) { 445 resetPixelStore = true; 446 } 447 } 448 } 449} 450 451void FontRenderer::checkTextureUpdate() { 452 if (!mUploadTexture) { 453 return; 454 } 455 456 Caches& caches = Caches::getInstance(); 457 GLuint lastTextureId = 0; 458 459 bool resetPixelStore = false; 460 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 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 char *text, 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, text, 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#ifdef ANDROID_ENABLE_RENDERSCRIPT 592 // Align buffers for renderscript usage 593 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 594 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 595 } 596 int size = paddedWidth * paddedHeight; 597 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 598#else 599 int size = paddedWidth * paddedHeight; 600 uint8_t* dataBuffer = (uint8_t*) malloc(size); 601#endif 602 603 memset(dataBuffer, 0, size); 604 605 int penX = intRadius - bounds.left; 606 int penY = intRadius - bounds.bottom; 607 608 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 609 // text has non-whitespace, so draw and blur to create the shadow 610 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 611 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 612 mCurrentFont->render(paint, text, numGlyphs, penX, penY, 613 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); 614 615 // Unbind any PBO we might have used 616 Caches::getInstance().pixelBufferState().unbind(); 617 618 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 619 } 620 621 image.width = paddedWidth; 622 image.height = paddedHeight; 623 image.image = dataBuffer; 624 image.penX = penX; 625 image.penY = penY; 626 627 return image; 628} 629 630void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) { 631 checkInit(); 632 633 mDrawn = false; 634 mBounds = bounds; 635 mFunctor = functor; 636 mClip = clip; 637} 638 639void FontRenderer::finishRender() { 640 mBounds = nullptr; 641 mClip = nullptr; 642 643 issueDrawCommand(); 644} 645 646void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, 647 const SkMatrix& matrix) { 648 Font* font = Font::create(this, paint, matrix); 649 font->precache(paint, text, numGlyphs); 650} 651 652void FontRenderer::endPrecaching() { 653 checkTextureUpdate(); 654} 655 656bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, 657 int numGlyphs, int x, int y, const float* positions, 658 Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { 659 if (!mCurrentFont) { 660 ALOGE("No font set"); 661 return false; 662 } 663 664 initRender(clip, bounds, functor); 665 mCurrentFont->render(paint, text, numGlyphs, x, y, positions); 666 667 if (forceFinish) { 668 finishRender(); 669 } 670 671 return mDrawn; 672} 673 674bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, 675 int numGlyphs, const SkPath* path, float hOffset, float vOffset, 676 Rect* bounds, TextDrawFunctor* functor) { 677 if (!mCurrentFont) { 678 ALOGE("No font set"); 679 return false; 680 } 681 682 initRender(clip, bounds, functor); 683 mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset); 684 finishRender(); 685 686 return mDrawn; 687} 688 689void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { 690 uint32_t intRadius = Blur::convertRadiusToInt(radius); 691#ifdef ANDROID_ENABLE_RENDERSCRIPT 692 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { 693 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 694 695 if (mRs == nullptr) { 696 mRs = new RSC::RS(); 697 // a null path is OK because there are no custom kernels used 698 // hence nothing gets cached by RS 699 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { 700 mRs.clear(); 701 ALOGE("blur RS failed to init"); 702 } else { 703 mRsElement = RSC::Element::A_8(mRs); 704 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); 705 } 706 } 707 if (mRs != nullptr) { 708 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 709 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, 710 RS_ALLOCATION_MIPMAP_NONE, 711 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 712 *image); 713 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, 714 RS_ALLOCATION_MIPMAP_NONE, 715 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 716 outImage); 717 718 mRsScript->setRadius(radius); 719 mRsScript->setInput(ain); 720 mRsScript->forEach(aout); 721 722 // replace the original image's pointer, avoiding a copy back to the original buffer 723 free(*image); 724 *image = outImage; 725 726 return; 727 } 728 } 729#endif 730 731 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); 732 Blur::generateGaussianWeights(gaussian.get(), radius); 733 734 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); 735 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); 736 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); 737} 738 739static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) { 740 uint32_t size = 0; 741 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 742 CacheTexture* cacheTexture = cacheTextures[i]; 743 if (cacheTexture && cacheTexture->getPixelBuffer()) { 744 size += cacheTexture->getPixelBuffer()->getSize(); 745 } 746 } 747 return size; 748} 749 750uint32_t FontRenderer::getCacheSize(GLenum format) const { 751 switch (format) { 752 case GL_ALPHA: { 753 return calculateCacheSize(mACacheTextures); 754 } 755 case GL_RGBA: { 756 return calculateCacheSize(mRGBACacheTextures); 757 } 758 default: { 759 return 0; 760 } 761 } 762} 763 764}; // namespace uirenderer 765}; // namespace android 766