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