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