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