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