FontRenderer.cpp revision 81997fef183074728b8981fd8e858b82ec70ed27
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(); 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(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 188void 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 203void FontRenderer::flushLargeCaches() { 204 flushLargeCaches(mACacheTextures); 205 flushLargeCaches(mRGBACacheTextures); 206} 207 208CacheTexture* 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 219void 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 398CacheTexture* 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 411void 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 432void FontRenderer::checkInit() { 433 if (mInitialized) { 434 return; 435 } 436 437 initTextTexture(); 438 439 mInitialized = true; 440} 441 442void 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 460void 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 487void 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 530void FontRenderer::issueDrawCommand() { 531 issueDrawCommand(mACacheTextures); 532 issueDrawCommand(mRGBACacheTextures); 533 534 mDrawn = true; 535} 536 537void 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 549void 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 572void 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 590void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) { 591 mCurrentFont = Font::create(this, paint, matrix); 592} 593 594FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, 595 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t 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 paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 617 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 618 619 uint32_t maxSize = Caches::getInstance().maxTextureSize; 620 if (paddedWidth > maxSize || paddedHeight > maxSize) { 621 return image; 622 } 623 624#ifdef ANDROID_ENABLE_RENDERSCRIPT 625 // Align buffers for renderscript usage 626 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 627 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 628 } 629 int size = paddedWidth * paddedHeight; 630 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 631#else 632 int size = paddedWidth * paddedHeight; 633 uint8_t* dataBuffer = (uint8_t*) malloc(size); 634#endif 635 636 memset(dataBuffer, 0, size); 637 638 int penX = radius - bounds.left; 639 int penY = radius - bounds.bottom; 640 641 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 642 // text has non-whitespace, so draw and blur to create the shadow 643 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 644 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 645 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 646 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 647 648 // Unbind any PBO we might have used 649 Caches::getInstance().unbindPixelBuffer(); 650 651 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 652 } 653 654 image.width = paddedWidth; 655 image.height = paddedHeight; 656 image.image = dataBuffer; 657 image.penX = penX; 658 image.penY = penY; 659 660 return image; 661} 662 663void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) { 664 checkInit(); 665 666 mDrawn = false; 667 mBounds = bounds; 668 mFunctor = functor; 669 mClip = clip; 670} 671 672void FontRenderer::finishRender() { 673 mBounds = NULL; 674 mClip = NULL; 675 676 issueDrawCommand(); 677} 678 679void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, 680 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(const 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(const SkPaint* paint, const Rect* clip, const char *text, 708 uint32_t startIndex, uint32_t len, int numGlyphs, const 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