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