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