FontRenderer.cpp revision cb4af9b10ab17187288d8f5ccb4cc1e465be9a51
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 = 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 78static bool sLogFontRendererCreate = true; 79 80FontRenderer::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 137void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) { 138 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 139 delete cacheTextures[i]; 140 } 141 cacheTextures.clear(); 142} 143 144FontRenderer::~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 155void 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 174void 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 189void FontRenderer::flushLargeCaches() { 190 flushLargeCaches(mACacheTextures); 191 flushLargeCaches(mRGBACacheTextures); 192} 193 194CacheTexture* 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 205void 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 384CacheTexture* 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 397void 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 418void FontRenderer::checkInit() { 419 if (mInitialized) { 420 return; 421 } 422 423 initTextTexture(); 424 425 mInitialized = true; 426} 427 428void 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 446void 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 473void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { 474 if (!mFunctor) return; 475 476 bool first = true; 477 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 478 CacheTexture* texture = cacheTextures[i]; 479 if (texture->canDraw()) { 480 if (first) { 481 checkTextureUpdate(); 482 first = false; 483 mDrawn = true; 484 } 485 486 mFunctor->draw(*texture, mLinearFiltering); 487 488 texture->resetMesh(); 489 } 490 } 491} 492 493void FontRenderer::issueDrawCommand() { 494 issueDrawCommand(mACacheTextures); 495 issueDrawCommand(mRGBACacheTextures); 496} 497 498void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 499 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 500 float x4, float y4, float u4, float v4, CacheTexture* texture) { 501 if (texture != mCurrentCacheTexture) { 502 // Now use the new texture id 503 mCurrentCacheTexture = texture; 504 } 505 506 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, 507 x3, y3, u3, v3, x4, y4, u4, v4); 508} 509 510void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 511 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 512 float x4, float y4, float u4, float v4, CacheTexture* texture) { 513 514 if (mClip && 515 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 516 return; 517 } 518 519 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 520 521 if (mBounds) { 522 mBounds->left = std::min(mBounds->left, x1); 523 mBounds->top = std::min(mBounds->top, y3); 524 mBounds->right = std::max(mBounds->right, x3); 525 mBounds->bottom = std::max(mBounds->bottom, y1); 526 } 527 528 if (mCurrentCacheTexture->endOfMesh()) { 529 issueDrawCommand(); 530 } 531} 532 533void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 534 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 535 float x4, float y4, float u4, float v4, CacheTexture* texture) { 536 537 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 538 539 if (mBounds) { 540 mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4)))); 541 mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4)))); 542 mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4)))); 543 mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4)))); 544 } 545 546 if (mCurrentCacheTexture->endOfMesh()) { 547 issueDrawCommand(); 548 } 549} 550 551void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { 552 mCurrentFont = Font::create(this, paint, matrix); 553} 554 555FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, 556 uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { 557 checkInit(); 558 559 DropShadow image; 560 image.width = 0; 561 image.height = 0; 562 image.image = nullptr; 563 image.penX = 0; 564 image.penY = 0; 565 566 if (!mCurrentFont) { 567 return image; 568 } 569 570 mDrawn = false; 571 mClip = nullptr; 572 mBounds = nullptr; 573 574 Rect bounds; 575 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 576 577 uint32_t intRadius = Blur::convertRadiusToInt(radius); 578 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; 579 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; 580 581 uint32_t maxSize = Caches::getInstance().maxTextureSize; 582 if (paddedWidth > maxSize || paddedHeight > maxSize) { 583 return image; 584 } 585 586#ifdef ANDROID_ENABLE_RENDERSCRIPT 587 // Align buffers for renderscript usage 588 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 589 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 590 } 591 int size = paddedWidth * paddedHeight; 592 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 593#else 594 int size = paddedWidth * paddedHeight; 595 uint8_t* dataBuffer = (uint8_t*) malloc(size); 596#endif 597 598 memset(dataBuffer, 0, size); 599 600 int penX = intRadius - bounds.left; 601 int penY = intRadius - bounds.bottom; 602 603 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 604 // text has non-whitespace, so draw and blur to create the shadow 605 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 606 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 607 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 608 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); 609 610 // Unbind any PBO we might have used 611 Caches::getInstance().pixelBufferState().unbind(); 612 613 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 614 } 615 616 image.width = paddedWidth; 617 image.height = paddedHeight; 618 image.image = dataBuffer; 619 image.penX = penX; 620 image.penY = penY; 621 622 return image; 623} 624 625void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) { 626 checkInit(); 627 628 mDrawn = false; 629 mBounds = bounds; 630 mFunctor = functor; 631 mClip = clip; 632} 633 634void FontRenderer::finishRender() { 635 mBounds = nullptr; 636 mClip = nullptr; 637 638 issueDrawCommand(); 639} 640 641void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, 642 const SkMatrix& matrix) { 643 Font* font = Font::create(this, paint, matrix); 644 font->precache(paint, text, numGlyphs); 645} 646 647void FontRenderer::endPrecaching() { 648 checkTextureUpdate(); 649} 650 651bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, 652 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 653 const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { 654 if (!mCurrentFont) { 655 ALOGE("No font set"); 656 return false; 657 } 658 659 initRender(clip, bounds, functor); 660 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 661 662 if (forceFinish) { 663 finishRender(); 664 } 665 666 return mDrawn; 667} 668 669bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, 670 uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, 671 float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) { 672 if (!mCurrentFont) { 673 ALOGE("No font set"); 674 return false; 675 } 676 677 initRender(clip, bounds, functor); 678 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 679 finishRender(); 680 681 return mDrawn; 682} 683 684void FontRenderer::removeFont(const Font* font) { 685 mActiveFonts.remove(font->getDescription()); 686 687 if (mCurrentFont == font) { 688 mCurrentFont = nullptr; 689 } 690} 691 692void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { 693 uint32_t intRadius = Blur::convertRadiusToInt(radius); 694#ifdef ANDROID_ENABLE_RENDERSCRIPT 695 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { 696 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 697 698 if (mRs == nullptr) { 699 mRs = new RSC::RS(); 700 // a null path is OK because there are no custom kernels used 701 // hence nothing gets cached by RS 702 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { 703 mRs.clear(); 704 ALOGE("blur RS failed to init"); 705 } else { 706 mRsElement = RSC::Element::A_8(mRs); 707 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); 708 } 709 } 710 if (mRs != nullptr) { 711 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 712 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, 713 RS_ALLOCATION_MIPMAP_NONE, 714 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 715 *image); 716 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, 717 RS_ALLOCATION_MIPMAP_NONE, 718 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 719 outImage); 720 721 mRsScript->setRadius(radius); 722 mRsScript->setInput(ain); 723 mRsScript->forEach(aout); 724 725 // replace the original image's pointer, avoiding a copy back to the original buffer 726 free(*image); 727 *image = outImage; 728 729 return; 730 } 731 } 732#endif 733 734 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); 735 Blur::generateGaussianWeights(gaussian.get(), intRadius); 736 737 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); 738 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); 739 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); 740} 741 742static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) { 743 uint32_t size = 0; 744 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 745 CacheTexture* cacheTexture = cacheTextures[i]; 746 if (cacheTexture && cacheTexture->getPixelBuffer()) { 747 size += cacheTexture->getPixelBuffer()->getSize(); 748 } 749 } 750 return size; 751} 752 753uint32_t FontRenderer::getCacheSize(GLenum format) const { 754 switch (format) { 755 case GL_ALPHA: { 756 return calculateCacheSize(mACacheTextures); 757 } 758 case GL_RGBA: { 759 return calculateCacheSize(mRGBACacheTextures); 760 } 761 default: { 762 return 0; 763 } 764 } 765} 766 767}; // namespace uirenderer 768}; // namespace android 769