FontRenderer.cpp revision 8d9b5fbdf0a005941b7f0019102cc7b94a41a2d6
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(paint->getColorFilter()); 76 renderer->setupDrawShader(paint->getShader()); 77 renderer->setupDrawBlending(paint); 78 renderer->setupDrawProgram(); 79 renderer->setupDrawModelView(kModelViewMode_Translate, false, 80 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate); 81 // Calling setupDrawTexture with the name 0 will enable the 82 // uv attributes and increase the texture unit count 83 // texture binding will be performed by the font renderer as 84 // needed 85 renderer->setupDrawTexture(0); 86 renderer->setupDrawPureColorUniforms(); 87 renderer->setupDrawColorFilterUniforms(paint->getColorFilter()); 88 renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate); 89 renderer->setupDrawTextGammaUniforms(); 90 91 return NO_ERROR; 92} 93 94/////////////////////////////////////////////////////////////////////////////// 95// FontRenderer 96/////////////////////////////////////////////////////////////////////////////// 97 98static bool sLogFontRendererCreate = true; 99 100FontRenderer::FontRenderer() : 101 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) { 102 103 if (sLogFontRendererCreate) { 104 INIT_LOGD("Creating FontRenderer"); 105 } 106 107 mGammaTable = NULL; 108 mInitialized = false; 109 110 mCurrentCacheTexture = NULL; 111 112 mLinearFiltering = false; 113 114 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH; 115 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT; 116 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH; 117 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT; 118 119 char property[PROPERTY_VALUE_MAX]; 120 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) { 121 mSmallCacheWidth = atoi(property); 122 } 123 124 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) { 125 mSmallCacheHeight = atoi(property); 126 } 127 128 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) { 129 mLargeCacheWidth = atoi(property); 130 } 131 132 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) { 133 mLargeCacheHeight = atoi(property); 134 } 135 136 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 137 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth; 138 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight; 139 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth; 140 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight; 141 142 if (sLogFontRendererCreate) { 143 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 144 mSmallCacheWidth, mSmallCacheHeight, 145 mLargeCacheWidth, mLargeCacheHeight >> 1, 146 mLargeCacheWidth, mLargeCacheHeight >> 1, 147 mLargeCacheWidth, mLargeCacheHeight); 148 } 149 150 sLogFontRendererCreate = false; 151} 152 153void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) { 154 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 155 delete cacheTextures[i]; 156 } 157 cacheTextures.clear(); 158} 159 160FontRenderer::~FontRenderer() { 161 clearCacheTextures(mACacheTextures); 162 clearCacheTextures(mRGBACacheTextures); 163 164 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 165 while (it.next()) { 166 delete it.value(); 167 } 168 mActiveFonts.clear(); 169} 170 171void FontRenderer::flushAllAndInvalidate() { 172 issueDrawCommand(); 173 174 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 175 while (it.next()) { 176 it.value()->invalidateTextureCache(); 177 } 178 179 for (uint32_t i = 0; i < mACacheTextures.size(); i++) { 180 mACacheTextures[i]->init(); 181 } 182 183 for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { 184 mRGBACacheTextures[i]->init(); 185 } 186 187 mDrawn = false; 188} 189 190void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) { 191 // Start from 1; don't deallocate smallest/default texture 192 for (uint32_t i = 1; i < cacheTextures.size(); i++) { 193 CacheTexture* cacheTexture = cacheTextures[i]; 194 if (cacheTexture->getPixelBuffer()) { 195 cacheTexture->init(); 196 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 197 while (it.next()) { 198 it.value()->invalidateTextureCache(cacheTexture); 199 } 200 cacheTexture->releaseTexture(); 201 } 202 } 203} 204 205void FontRenderer::flushLargeCaches() { 206 flushLargeCaches(mACacheTextures); 207 flushLargeCaches(mRGBACacheTextures); 208} 209 210CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures, 211 const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) { 212 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 213 if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) { 214 return cacheTextures[i]; 215 } 216 } 217 // Could not fit glyph into current cache textures 218 return NULL; 219} 220 221void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 222 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { 223 checkInit(); 224 225 // If the glyph bitmap is empty let's assum the glyph is valid 226 // so we can avoid doing extra work later on 227 if (glyph.fWidth == 0 || glyph.fHeight == 0) { 228 cachedGlyph->mIsValid = true; 229 cachedGlyph->mCacheTexture = NULL; 230 return; 231 } 232 233 cachedGlyph->mIsValid = false; 234 235 // choose an appropriate cache texture list for this glyph format 236 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); 237 Vector<CacheTexture*>* cacheTextures = NULL; 238 switch (format) { 239 case SkMask::kA8_Format: 240 case SkMask::kBW_Format: 241 cacheTextures = &mACacheTextures; 242 break; 243 case SkMask::kARGB32_Format: 244 cacheTextures = &mRGBACacheTextures; 245 break; 246 default: 247#if DEBUG_FONT_RENDERER 248 ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format); 249#endif 250 return; 251 } 252 253 // If the glyph is too tall, don't cache it 254 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > 255 (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) { 256 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 257 (int) glyph.fWidth, (int) glyph.fHeight); 258 return; 259 } 260 261 // Now copy the bitmap into the cache texture 262 uint32_t startX = 0; 263 uint32_t startY = 0; 264 265 CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY); 266 267 if (!cacheTexture) { 268 if (!precaching) { 269 // If the new glyph didn't fit and we are not just trying to precache it, 270 // clear out the cache and try again 271 flushAllAndInvalidate(); 272 cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY); 273 } 274 275 if (!cacheTexture) { 276 // either the glyph didn't fit or we're precaching and will cache it when we draw 277 return; 278 } 279 } 280 281 cachedGlyph->mCacheTexture = cacheTexture; 282 283 *retOriginX = startX; 284 *retOriginY = startY; 285 286 uint32_t endX = startX + glyph.fWidth; 287 uint32_t endY = startY + glyph.fHeight; 288 289 uint32_t cacheWidth = cacheTexture->getWidth(); 290 291 if (!cacheTexture->getPixelBuffer()) { 292 Caches::getInstance().activeTexture(0); 293 // Large-glyph texture memory is allocated only as needed 294 cacheTexture->allocateTexture(); 295 } 296 if (!cacheTexture->mesh()) { 297 cacheTexture->allocateMesh(); 298 } 299 300 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map(); 301 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 302 int srcStride = glyph.rowBytes(); 303 304 // Copy the glyph image, taking the mask format into account 305 switch (format) { 306 case SkMask::kA8_Format: { 307 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 308 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX 309 - TEXTURE_BORDER_SIZE; 310 // write leading border line 311 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 312 // write glyph data 313 if (mGammaTable) { 314 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 315 row = cacheY * cacheWidth; 316 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 317 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 318 uint8_t tempCol = bitmapBuffer[bY + bX]; 319 cacheBuffer[row + cacheX] = mGammaTable[tempCol]; 320 } 321 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 322 } 323 } else { 324 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 325 row = cacheY * cacheWidth; 326 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); 327 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 328 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 329 } 330 } 331 // write trailing border line 332 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; 333 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 334 break; 335 } 336 case SkMask::kARGB32_Format: { 337 // prep data lengths 338 const size_t formatSize = PixelBuffer::formatSize(GL_RGBA); 339 const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE; 340 size_t rowSize = formatSize * glyph.fWidth; 341 // prep advances 342 size_t dstStride = formatSize * cacheWidth; 343 // prep indices 344 // - we actually start one row early, and then increment before first copy 345 uint8_t* src = &bitmapBuffer[0 - srcStride]; 346 uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)]; 347 uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)]; 348 uint8_t* dstL = dst - borderSize; 349 uint8_t* dstR = dst + rowSize; 350 // write leading border line 351 memset(dstL, 0, rowSize + 2 * borderSize); 352 // write glyph data 353 while (dst < dstEnd) { 354 memset(dstL += dstStride, 0, borderSize); // leading border column 355 memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data 356 memset(dstR += dstStride, 0, borderSize); // trailing border column 357 } 358 // write trailing border line 359 memset(dstL += dstStride, 0, rowSize + 2 * borderSize); 360 break; 361 } 362 case SkMask::kBW_Format: { 363 uint32_t cacheX = 0, cacheY = 0; 364 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX 365 - TEXTURE_BORDER_SIZE; 366 static const uint8_t COLORS[2] = { 0, 255 }; 367 // write leading border line 368 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 369 // write glyph data 370 for (cacheY = startY; cacheY < endY; cacheY++) { 371 cacheX = startX; 372 int rowBytes = srcStride; 373 uint8_t* buffer = bitmapBuffer; 374 375 row = cacheY * cacheWidth; 376 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 377 while (--rowBytes >= 0) { 378 uint8_t b = *buffer++; 379 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { 380 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; 381 } 382 } 383 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 384 385 bitmapBuffer += srcStride; 386 } 387 // write trailing border line 388 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; 389 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 390 break; 391 } 392 default: 393 ALOGW("Unknown glyph format: 0x%x", format); 394 break; 395 } 396 397 cachedGlyph->mIsValid = true; 398} 399 400CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, 401 bool allocate) { 402 CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads); 403 404 if (allocate) { 405 Caches::getInstance().activeTexture(0); 406 cacheTexture->allocateTexture(); 407 cacheTexture->allocateMesh(); 408 } 409 410 return cacheTexture; 411} 412 413void FontRenderer::initTextTexture() { 414 clearCacheTextures(mACacheTextures); 415 clearCacheTextures(mRGBACacheTextures); 416 417 mUploadTexture = false; 418 mACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 419 GL_ALPHA, true)); 420 mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 421 GL_ALPHA, false)); 422 mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 423 GL_ALPHA, false)); 424 mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, 425 GL_ALPHA, false)); 426 mRGBACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 427 GL_RGBA, false)); 428 mRGBACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 429 GL_RGBA, false)); 430 mCurrentCacheTexture = mACacheTextures[0]; 431} 432 433// We don't want to allocate anything unless we actually draw text 434void FontRenderer::checkInit() { 435 if (mInitialized) { 436 return; 437 } 438 439 initTextTexture(); 440 441 mInitialized = true; 442} 443 444void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheTextures, 445 bool& resetPixelStore, GLuint& lastTextureId) { 446 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 447 CacheTexture* cacheTexture = cacheTextures[i]; 448 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { 449 if (cacheTexture->getTextureId() != lastTextureId) { 450 lastTextureId = cacheTexture->getTextureId(); 451 caches.activeTexture(0); 452 caches.bindTexture(lastTextureId); 453 } 454 455 if (cacheTexture->upload()) { 456 resetPixelStore = true; 457 } 458 } 459 } 460} 461 462void FontRenderer::checkTextureUpdate() { 463 if (!mUploadTexture) { 464 return; 465 } 466 467 Caches& caches = Caches::getInstance(); 468 GLuint lastTextureId = 0; 469 470 bool resetPixelStore = false; 471 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 472 473 // Iterate over all the cache textures and see which ones need to be updated 474 checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId); 475 checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId); 476 477 // Unbind any PBO we might have used to update textures 478 caches.unbindPixelBuffer(); 479 480 // Reset to default unpack row length to avoid affecting texture 481 // uploads in other parts of the renderer 482 if (resetPixelStore) { 483 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 484 } 485 486 mUploadTexture = false; 487} 488 489void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { 490 Caches& caches = Caches::getInstance(); 491 bool first = true; 492 bool force = false; 493 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 494 CacheTexture* texture = cacheTextures[i]; 495 if (texture->canDraw()) { 496 if (first) { 497 if (mFunctor) { 498 TextSetupFunctor::Data functorData(texture->getFormat()); 499 (*mFunctor)(0, &functorData); 500 } 501 502 checkTextureUpdate(); 503 caches.bindQuadIndicesBuffer(); 504 505 if (!mDrawn) { 506 // If returns true, a VBO was bound and we must 507 // rebind our vertex attrib pointers even if 508 // they have the same values as the current pointers 509 force = caches.unbindMeshBuffer(); 510 } 511 512 caches.activeTexture(0); 513 first = false; 514 } 515 516 caches.bindTexture(texture->getTextureId()); 517 texture->setLinearFiltering(mLinearFiltering, false); 518 519 TextureVertex* mesh = texture->mesh(); 520 caches.bindPositionVertexPointer(force, &mesh[0].x); 521 caches.bindTexCoordsVertexPointer(force, &mesh[0].u); 522 force = false; 523 524 glDrawElements(GL_TRIANGLES, texture->meshElementCount(), 525 GL_UNSIGNED_SHORT, texture->indices()); 526 527 texture->resetMesh(); 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 = NULL; 604 image.penX = 0; 605 image.penY = 0; 606 607 if (!mCurrentFont) { 608 return image; 609 } 610 611 mDrawn = false; 612 mClip = NULL; 613 mBounds = NULL; 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, NULL, positions); 650 651 // Unbind any PBO we might have used 652 Caches::getInstance().unbindPixelBuffer(); 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, Functor* functor) { 667 checkInit(); 668 669 mDrawn = false; 670 mBounds = bounds; 671 mFunctor = functor; 672 mClip = clip; 673} 674 675void FontRenderer::finishRender() { 676 mBounds = NULL; 677 mClip = NULL; 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, Functor* 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, Functor* 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 = NULL; 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 == 0) { 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 != 0) { 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 float *gaussian = new float[2 * intRadius + 1]; 776 Blur::generateGaussianWeights(gaussian, radius); 777 778 uint8_t* scratch = new uint8_t[width * height]; 779 Blur::horizontal(gaussian, intRadius, *image, scratch, width, height); 780 Blur::vertical(gaussian, intRadius, scratch, *image, width, height); 781 782 delete[] gaussian; 783 delete[] scratch; 784} 785 786static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) { 787 uint32_t size = 0; 788 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 789 CacheTexture* cacheTexture = cacheTextures[i]; 790 if (cacheTexture && cacheTexture->getPixelBuffer()) { 791 size += cacheTexture->getPixelBuffer()->getSize(); 792 } 793 } 794 return size; 795} 796 797uint32_t FontRenderer::getCacheSize(GLenum format) const { 798 switch (format) { 799 case GL_ALPHA: { 800 return calculateCacheSize(mACacheTextures); 801 } 802 case GL_RGBA: { 803 return calculateCacheSize(mRGBACacheTextures); 804 } 805 default: { 806 return 0; 807 } 808 } 809} 810 811}; // namespace uirenderer 812}; // namespace android 813