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