FontRenderer.cpp revision b969a0de65730b071d846f8302e751e2637e6dbe
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 <SkUtils.h> 20 21#include <cutils/properties.h> 22 23#include <utils/Log.h> 24 25#include "Caches.h" 26#include "Debug.h" 27#include "FontRenderer.h" 28#include "Rect.h" 29 30namespace android { 31namespace uirenderer { 32 33/////////////////////////////////////////////////////////////////////////////// 34// FontRenderer 35/////////////////////////////////////////////////////////////////////////////// 36 37static bool sLogFontRendererCreate = true; 38 39FontRenderer::FontRenderer() : 40 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) { 41 42 if (sLogFontRendererCreate) { 43 INIT_LOGD("Creating FontRenderer"); 44 } 45 46 mGammaTable = NULL; 47 mInitialized = false; 48 mMaxNumberOfQuads = 1024; 49 mCurrentQuadIndex = 0; 50 mLastQuadIndex = 0; 51 52 mTextMesh = NULL; 53 mCurrentCacheTexture = NULL; 54 55 mLinearFiltering = false; 56 57 mIndexBufferID = 0; 58 59 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH; 60 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT; 61 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH; 62 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT; 63 64 char property[PROPERTY_VALUE_MAX]; 65 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) { 66 mSmallCacheWidth = atoi(property); 67 } 68 69 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) { 70 mSmallCacheHeight = atoi(property); 71 } 72 73 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) { 74 mLargeCacheWidth = atoi(property); 75 } 76 77 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) { 78 mLargeCacheHeight = atoi(property); 79 } 80 81 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 82 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth; 83 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight; 84 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth; 85 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight; 86 87 if (sLogFontRendererCreate) { 88 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 89 mSmallCacheWidth, mSmallCacheHeight, 90 mLargeCacheWidth, mLargeCacheHeight >> 1, 91 mLargeCacheWidth, mLargeCacheHeight >> 1, 92 mLargeCacheWidth, mLargeCacheHeight); 93 } 94 95 sLogFontRendererCreate = false; 96} 97 98FontRenderer::~FontRenderer() { 99 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 100 delete mCacheTextures[i]; 101 } 102 mCacheTextures.clear(); 103 104 if (mInitialized) { 105 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 106 Caches::getInstance().unbindIndicesBuffer(); 107 glDeleteBuffers(1, &mIndexBufferID); 108 109 delete[] mTextMesh; 110 } 111 112 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 113 while (it.next()) { 114 delete it.value(); 115 } 116 mActiveFonts.clear(); 117} 118 119void FontRenderer::flushAllAndInvalidate() { 120 if (mCurrentQuadIndex != 0) { 121 issueDrawCommand(); 122 } 123 124 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 125 while (it.next()) { 126 it.value()->invalidateTextureCache(); 127 } 128 129 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 130 mCacheTextures[i]->init(); 131 } 132 133#if DEBUG_FONT_RENDERER 134 uint16_t totalGlyphs = 0; 135 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 136 totalGlyphs += mCacheTextures[i]->getGlyphCount(); 137 // Erase caches, just as a debugging facility 138 if (mCacheTextures[i]->getTexture()) { 139 memset(mCacheTextures[i]->getTexture(), 0, 140 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight()); 141 } 142 } 143 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs); 144#endif 145} 146 147void FontRenderer::flushLargeCaches() { 148 // Start from 1; don't deallocate smallest/default texture 149 for (uint32_t i = 1; i < mCacheTextures.size(); i++) { 150 CacheTexture* cacheTexture = mCacheTextures[i]; 151 if (cacheTexture->getTexture()) { 152 cacheTexture->init(); 153 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 154 while (it.next()) { 155 it.value()->invalidateTextureCache(cacheTexture); 156 } 157 cacheTexture->releaseTexture(); 158 } 159 } 160} 161 162CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph, 163 uint32_t* startX, uint32_t* startY) { 164 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 165 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) { 166 return mCacheTextures[i]; 167 } 168 } 169 // Could not fit glyph into current cache textures 170 return NULL; 171} 172 173void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 174 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { 175 checkInit(); 176 cachedGlyph->mIsValid = false; 177 // If the glyph is too tall, don't cache it 178 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > 179 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) { 180 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 181 (int) glyph.fWidth, (int) glyph.fHeight); 182 return; 183 } 184 185 // Now copy the bitmap into the cache texture 186 uint32_t startX = 0; 187 uint32_t startY = 0; 188 189 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 190 191 if (!cacheTexture) { 192 if (!precaching) { 193 // If the new glyph didn't fit and we are not just trying to precache it, 194 // clear out the cache and try again 195 flushAllAndInvalidate(); 196 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 197 } 198 199 if (!cacheTexture) { 200 // either the glyph didn't fit or we're precaching and will cache it when we draw 201 return; 202 } 203 } 204 205 cachedGlyph->mCacheTexture = cacheTexture; 206 207 *retOriginX = startX; 208 *retOriginY = startY; 209 210 uint32_t endX = startX + glyph.fWidth; 211 uint32_t endY = startY + glyph.fHeight; 212 213 uint32_t cacheWidth = cacheTexture->getWidth(); 214 215 if (!cacheTexture->getTexture()) { 216 Caches::getInstance().activeTexture(0); 217 // Large-glyph texture memory is allocated only as needed 218 cacheTexture->allocateTexture(); 219 } 220 221 // Tells us whether the glyphs is B&W (1 bit per pixel) 222 // or anti-aliased (8 bits per pixel) 223 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); 224 225 uint8_t* cacheBuffer = cacheTexture->getTexture(); 226 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 227 228 // Zero out the borders 229 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) { 230 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0; 231 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0; 232 } 233 234 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1; 235 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) { 236 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0; 237 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0; 238 } 239 240 // Copy the glyph image, taking the mask format into account 241 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 242 int stride = glyph.rowBytes(); 243 244 switch (format) { 245 case SkMask::kA8_Format: { 246 if (mGammaTable) { 247 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) { 248 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 249 uint8_t tempCol = bitmapBuffer[bY + bX]; 250 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 251 } 252 } 253 } else { 254 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) { 255 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY], 256 glyph.fWidth); 257 } 258 } 259 break; 260 } 261 case SkMask::kBW_Format: { 262 static const uint8_t COLORS[2] = { 0, 255 }; 263 264 for (cacheY = startY; cacheY < endY; cacheY++) { 265 cacheX = startX; 266 int rowBytes = stride; 267 uint8_t* buffer = bitmapBuffer; 268 269 while (--rowBytes >= 0) { 270 uint8_t b = *buffer++; 271 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { 272 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; 273 } 274 } 275 276 bitmapBuffer += stride; 277 } 278 break; 279 } 280 default: 281 ALOGW("Unkown glyph format: 0x%x", format); 282 break; 283 } 284 285 cachedGlyph->mIsValid = true; 286} 287 288CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 289 CacheTexture* cacheTexture = new CacheTexture(width, height); 290 291 if (allocate) { 292 Caches::getInstance().activeTexture(0); 293 cacheTexture->allocateTexture(); 294 } 295 296 return cacheTexture; 297} 298 299void FontRenderer::initTextTexture() { 300 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 301 delete mCacheTextures[i]; 302 } 303 mCacheTextures.clear(); 304 305 mUploadTexture = false; 306 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true)); 307 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false)); 308 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false)); 309 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false)); 310 mCurrentCacheTexture = mCacheTextures[0]; 311} 312 313// Avoid having to reallocate memory and render quad by quad 314void FontRenderer::initVertexArrayBuffers() { 315 uint32_t numIndices = mMaxNumberOfQuads * 6; 316 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 317 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 318 319 // Four verts, two triangles , six indices per quad 320 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 321 int i6 = i * 6; 322 int i4 = i * 4; 323 324 indexBufferData[i6 + 0] = i4 + 0; 325 indexBufferData[i6 + 1] = i4 + 1; 326 indexBufferData[i6 + 2] = i4 + 2; 327 328 indexBufferData[i6 + 3] = i4 + 0; 329 indexBufferData[i6 + 4] = i4 + 2; 330 indexBufferData[i6 + 5] = i4 + 3; 331 } 332 333 glGenBuffers(1, &mIndexBufferID); 334 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 335 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 336 337 free(indexBufferData); 338 339 uint32_t coordSize = 2; 340 uint32_t uvSize = 2; 341 uint32_t vertsPerQuad = 4; 342 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 343 mTextMesh = new float[vertexBufferSize]; 344} 345 346// We don't want to allocate anything unless we actually draw text 347void FontRenderer::checkInit() { 348 if (mInitialized) { 349 return; 350 } 351 352 initTextTexture(); 353 initVertexArrayBuffers(); 354 355 mInitialized = true; 356} 357 358void FontRenderer::updateDrawParams() { 359 if (mCurrentQuadIndex != mLastQuadIndex) { 360 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6)); 361 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex); 362 mDrawCacheTextures.add(mCurrentCacheTexture); 363 mLastQuadIndex = mCurrentQuadIndex; 364 } 365} 366 367void FontRenderer::checkTextureUpdate() { 368 if (!mUploadTexture) { 369 return; 370 } 371 372 Caches& caches = Caches::getInstance(); 373 GLuint lastTextureId = 0; 374 // Iterate over all the cache textures and see which ones need to be updated 375 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 376 CacheTexture* cacheTexture = mCacheTextures[i]; 377 if (cacheTexture->isDirty() && cacheTexture->getTexture()) { 378 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer 379 // of data. So expand the dirty rect to the encompassing horizontal stripe. 380 const Rect* dirtyRect = cacheTexture->getDirtyRect(); 381 uint32_t x = 0; 382 uint32_t y = dirtyRect->top; 383 uint32_t width = cacheTexture->getWidth(); 384 uint32_t height = dirtyRect->getHeight(); 385 void* textureData = cacheTexture->getTexture() + y * width; 386 387 if (cacheTexture->getTextureId() != lastTextureId) { 388 lastTextureId = cacheTexture->getTextureId(); 389 caches.activeTexture(0); 390 glBindTexture(GL_TEXTURE_2D, lastTextureId); 391 } 392#if DEBUG_FONT_RENDERER 393 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d", 394 i, x, y, width, height); 395#endif 396 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 397 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 398 cacheTexture->setDirty(false); 399 } 400 } 401 402 mUploadTexture = false; 403} 404 405void FontRenderer::issueDrawCommand() { 406 updateDrawParams(); 407 checkTextureUpdate(); 408 409 Caches& caches = Caches::getInstance(); 410 caches.bindIndicesBuffer(mIndexBufferID); 411 if (!mDrawn) { 412 float* buffer = mTextMesh; 413 int offset = 2; 414 415 bool force = caches.unbindMeshBuffer(); 416 caches.bindPositionVertexPointer(force, buffer); 417 caches.bindTexCoordsVertexPointer(force, buffer + offset); 418 } 419 420 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) { 421 uint16_t* offset = mDrawOffsets[i]; 422 uint32_t count = mDrawCounts[i]; 423 CacheTexture* texture = mDrawCacheTextures[i]; 424 425 caches.activeTexture(0); 426 glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); 427 428 texture->setLinearFiltering(mLinearFiltering, false); 429 430 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset); 431 } 432 433 mDrawn = true; 434 435 mCurrentQuadIndex = 0; 436 mLastQuadIndex = 0; 437 mDrawOffsets.clear(); 438 mDrawCounts.clear(); 439 mDrawCacheTextures.clear(); 440} 441 442void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 443 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 444 float x4, float y4, float u4, float v4, CacheTexture* texture) { 445 if (texture != mCurrentCacheTexture) { 446 updateDrawParams(); 447 // Now use the new texture id 448 mCurrentCacheTexture = texture; 449 } 450 451 const uint32_t vertsPerQuad = 4; 452 const uint32_t floatsPerVert = 4; 453 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 454 455 (*currentPos++) = x1; 456 (*currentPos++) = y1; 457 (*currentPos++) = u1; 458 (*currentPos++) = v1; 459 460 (*currentPos++) = x2; 461 (*currentPos++) = y2; 462 (*currentPos++) = u2; 463 (*currentPos++) = v2; 464 465 (*currentPos++) = x3; 466 (*currentPos++) = y3; 467 (*currentPos++) = u3; 468 (*currentPos++) = v3; 469 470 (*currentPos++) = x4; 471 (*currentPos++) = y4; 472 (*currentPos++) = u4; 473 (*currentPos++) = v4; 474 475 mCurrentQuadIndex++; 476} 477 478void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 479 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 480 float x4, float y4, float u4, float v4, CacheTexture* texture) { 481 482 if (mClip && 483 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 484 return; 485 } 486 487 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 488 489 if (mBounds) { 490 mBounds->left = fmin(mBounds->left, x1); 491 mBounds->top = fmin(mBounds->top, y3); 492 mBounds->right = fmax(mBounds->right, x3); 493 mBounds->bottom = fmax(mBounds->bottom, y1); 494 } 495 496 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 497 issueDrawCommand(); 498 } 499} 500 501void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 502 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 503 float x4, float y4, float u4, float v4, CacheTexture* texture) { 504 505 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 506 507 if (mBounds) { 508 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 509 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 510 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 511 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 512 } 513 514 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 515 issueDrawCommand(); 516 } 517} 518 519void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) { 520 mCurrentFont = Font::create(this, paint, matrix); 521} 522 523FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 524 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 525 checkInit(); 526 527 if (!mCurrentFont) { 528 DropShadow image; 529 image.width = 0; 530 image.height = 0; 531 image.image = NULL; 532 image.penX = 0; 533 image.penY = 0; 534 return image; 535 } 536 537 mDrawn = false; 538 mClip = NULL; 539 mBounds = NULL; 540 541 Rect bounds; 542 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 543 544 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 545 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 546 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 547 548 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 549 dataBuffer[i] = 0; 550 } 551 552 int penX = radius - bounds.left; 553 int penY = radius - bounds.bottom; 554 555 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 556 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 557 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 558 559 DropShadow image; 560 image.width = paddedWidth; 561 image.height = paddedHeight; 562 image.image = dataBuffer; 563 image.penX = penX; 564 image.penY = penY; 565 566 return image; 567} 568 569void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 570 checkInit(); 571 572 mDrawn = false; 573 mBounds = bounds; 574 mClip = clip; 575} 576 577void FontRenderer::finishRender() { 578 mBounds = NULL; 579 mClip = NULL; 580 581 if (mCurrentQuadIndex != 0) { 582 issueDrawCommand(); 583 } 584} 585 586void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) { 587 Font* font = Font::create(this, paint, matrix); 588 font->precache(paint, text, numGlyphs); 589} 590 591bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 592 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 593 const float* positions, Rect* bounds) { 594 if (!mCurrentFont) { 595 ALOGE("No font set"); 596 return false; 597 } 598 599 initRender(clip, bounds); 600 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 601 finishRender(); 602 603 return mDrawn; 604} 605 606bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 607 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 608 float hOffset, float vOffset, Rect* bounds) { 609 if (!mCurrentFont) { 610 ALOGE("No font set"); 611 return false; 612 } 613 614 initRender(clip, bounds); 615 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 616 finishRender(); 617 618 return mDrawn; 619} 620 621void FontRenderer::removeFont(const Font* font) { 622 mActiveFonts.remove(font->getDescription()); 623 624 if (mCurrentFont == font) { 625 mCurrentFont = NULL; 626 } 627} 628 629void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 630 // Compute gaussian weights for the blur 631 // e is the euler's number 632 float e = 2.718281828459045f; 633 float pi = 3.1415926535897932f; 634 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 635 // x is of the form [-radius .. 0 .. radius] 636 // and sigma varies with radius. 637 // Based on some experimental radius values and sigma's 638 // we approximately fit sigma = f(radius) as 639 // sigma = radius * 0.3 + 0.6 640 // The larger the radius gets, the more our gaussian blur 641 // will resemble a box blur since with large sigma 642 // the gaussian curve begins to lose its shape 643 float sigma = 0.3f * (float) radius + 0.6f; 644 645 // Now compute the coefficints 646 // We will store some redundant values to save some math during 647 // the blur calculations 648 // precompute some values 649 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 650 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 651 652 float normalizeFactor = 0.0f; 653 for (int32_t r = -radius; r <= radius; r ++) { 654 float floatR = (float) r; 655 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 656 normalizeFactor += weights[r + radius]; 657 } 658 659 //Now we need to normalize the weights because all our coefficients need to add up to one 660 normalizeFactor = 1.0f / normalizeFactor; 661 for (int32_t r = -radius; r <= radius; r ++) { 662 weights[r + radius] *= normalizeFactor; 663 } 664} 665 666void FontRenderer::horizontalBlur(float* weights, int32_t radius, 667 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 668 float blurredPixel = 0.0f; 669 float currentPixel = 0.0f; 670 671 for (int32_t y = 0; y < height; y ++) { 672 673 const uint8_t* input = source + y * width; 674 uint8_t* output = dest + y * width; 675 676 for (int32_t x = 0; x < width; x ++) { 677 blurredPixel = 0.0f; 678 const float* gPtr = weights; 679 // Optimization for non-border pixels 680 if (x > radius && x < (width - radius)) { 681 const uint8_t *i = input + (x - radius); 682 for (int r = -radius; r <= radius; r ++) { 683 currentPixel = (float) (*i); 684 blurredPixel += currentPixel * gPtr[0]; 685 gPtr++; 686 i++; 687 } 688 } else { 689 for (int32_t r = -radius; r <= radius; r ++) { 690 // Stepping left and right away from the pixel 691 int validW = x + r; 692 if (validW < 0) { 693 validW = 0; 694 } 695 if (validW > width - 1) { 696 validW = width - 1; 697 } 698 699 currentPixel = (float) input[validW]; 700 blurredPixel += currentPixel * gPtr[0]; 701 gPtr++; 702 } 703 } 704 *output = (uint8_t)blurredPixel; 705 output ++; 706 } 707 } 708} 709 710void FontRenderer::verticalBlur(float* weights, int32_t radius, 711 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 712 float blurredPixel = 0.0f; 713 float currentPixel = 0.0f; 714 715 for (int32_t y = 0; y < height; y ++) { 716 uint8_t* output = dest + y * width; 717 718 for (int32_t x = 0; x < width; x ++) { 719 blurredPixel = 0.0f; 720 const float* gPtr = weights; 721 const uint8_t* input = source + x; 722 // Optimization for non-border pixels 723 if (y > radius && y < (height - radius)) { 724 const uint8_t *i = input + ((y - radius) * width); 725 for (int32_t r = -radius; r <= radius; r ++) { 726 currentPixel = (float)(*i); 727 blurredPixel += currentPixel * gPtr[0]; 728 gPtr++; 729 i += width; 730 } 731 } else { 732 for (int32_t r = -radius; r <= radius; r ++) { 733 int validH = y + r; 734 // Clamp to zero and width 735 if (validH < 0) { 736 validH = 0; 737 } 738 if (validH > height - 1) { 739 validH = height - 1; 740 } 741 742 const uint8_t *i = input + validH * width; 743 currentPixel = (float) (*i); 744 blurredPixel += currentPixel * gPtr[0]; 745 gPtr++; 746 } 747 } 748 *output = (uint8_t) blurredPixel; 749 output++; 750 } 751 } 752} 753 754 755void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 756 float *gaussian = new float[2 * radius + 1]; 757 computeGaussianWeights(gaussian, radius); 758 759 uint8_t* scratch = new uint8_t[width * height]; 760 761 horizontalBlur(gaussian, radius, image, scratch, width, height); 762 verticalBlur(gaussian, radius, scratch, image, width, height); 763 764 delete[] gaussian; 765 delete[] scratch; 766} 767 768}; // namespace uirenderer 769}; // namespace android 770