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