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