FontRenderer.cpp revision b92d8f7979c29c7c09932578a11b2f8d6eec1d90
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 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer 335 // of data. So expand the dirty rect to the encompassing horizontal stripe. 336 const Rect* dirtyRect = cacheTexture->getDirtyRect(); 337 uint32_t x = 0; 338 uint32_t y = dirtyRect->top; 339 uint32_t width = cacheTexture->getWidth(); 340 uint32_t height = dirtyRect->getHeight(); 341 void* textureData = cacheTexture->getTexture() + y * width; 342 343 if (cacheTexture->getTextureId() != lastTextureId) { 344 lastTextureId = cacheTexture->getTextureId(); 345 caches.activeTexture(0); 346 glBindTexture(GL_TEXTURE_2D, lastTextureId); 347 } 348#if DEBUG_FONT_RENDERER 349 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d", 350 i, x, y, width, height); 351#endif 352 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 353 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 354 cacheTexture->setDirty(false); 355 } 356 } 357 358 caches.activeTexture(0); 359 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId()); 360 361 mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false); 362 mLastCacheTexture = mCurrentCacheTexture; 363 364 mUploadTexture = false; 365} 366 367void FontRenderer::issueDrawCommand() { 368 checkTextureUpdate(); 369 370 Caches& caches = Caches::getInstance(); 371 caches.bindIndicesBuffer(mIndexBufferID); 372 if (!mDrawn) { 373 float* buffer = mTextMesh; 374 int offset = 2; 375 376 bool force = caches.unbindMeshBuffer(); 377 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 378 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 379 buffer + offset); 380 } 381 382 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 383 384 mDrawn = true; 385} 386 387void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 388 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 389 float x4, float y4, float u4, float v4, CacheTexture* texture) { 390 if (texture != mCurrentCacheTexture) { 391 if (mCurrentQuadIndex != 0) { 392 // First, draw everything stored already which uses the previous texture 393 issueDrawCommand(); 394 mCurrentQuadIndex = 0; 395 } 396 // Now use the new texture id 397 mCurrentCacheTexture = texture; 398 } 399 400 const uint32_t vertsPerQuad = 4; 401 const uint32_t floatsPerVert = 4; 402 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 403 404 (*currentPos++) = x1; 405 (*currentPos++) = y1; 406 (*currentPos++) = u1; 407 (*currentPos++) = v1; 408 409 (*currentPos++) = x2; 410 (*currentPos++) = y2; 411 (*currentPos++) = u2; 412 (*currentPos++) = v2; 413 414 (*currentPos++) = x3; 415 (*currentPos++) = y3; 416 (*currentPos++) = u3; 417 (*currentPos++) = v3; 418 419 (*currentPos++) = x4; 420 (*currentPos++) = y4; 421 (*currentPos++) = u4; 422 (*currentPos++) = v4; 423 424 mCurrentQuadIndex++; 425} 426 427void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 428 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 429 float x4, float y4, float u4, float v4, CacheTexture* texture) { 430 431 if (mClip && 432 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 433 return; 434 } 435 436 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 437 438 if (mBounds) { 439 mBounds->left = fmin(mBounds->left, x1); 440 mBounds->top = fmin(mBounds->top, y3); 441 mBounds->right = fmax(mBounds->right, x3); 442 mBounds->bottom = fmax(mBounds->bottom, y1); 443 } 444 445 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 446 issueDrawCommand(); 447 mCurrentQuadIndex = 0; 448 } 449} 450 451void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 452 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 453 float x4, float y4, float u4, float v4, CacheTexture* texture) { 454 455 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 456 457 if (mBounds) { 458 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 459 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 460 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 461 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 462 } 463 464 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 465 issueDrawCommand(); 466 mCurrentQuadIndex = 0; 467 } 468} 469 470void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 471 int flags = 0; 472 if (paint->isFakeBoldText()) { 473 flags |= Font::kFakeBold; 474 } 475 476 const float skewX = paint->getTextSkewX(); 477 uint32_t italicStyle = *(uint32_t*) &skewX; 478 const float scaleXFloat = paint->getTextScaleX(); 479 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 480 SkPaint::Style style = paint->getStyle(); 481 const float strokeWidthFloat = paint->getStrokeWidth(); 482 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 483 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 484 scaleX, style, strokeWidth); 485 486} 487 488FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 489 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 490 checkInit(); 491 492 if (!mCurrentFont) { 493 DropShadow image; 494 image.width = 0; 495 image.height = 0; 496 image.image = NULL; 497 image.penX = 0; 498 image.penY = 0; 499 return image; 500 } 501 502 mDrawn = false; 503 mClip = NULL; 504 mBounds = NULL; 505 506 Rect bounds; 507 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 508 509 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 510 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 511 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 512 513 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 514 dataBuffer[i] = 0; 515 } 516 517 int penX = radius - bounds.left; 518 int penY = radius - bounds.bottom; 519 520 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 521 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 522 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 523 524 DropShadow image; 525 image.width = paddedWidth; 526 image.height = paddedHeight; 527 image.image = dataBuffer; 528 image.penX = penX; 529 image.penY = penY; 530 531 return image; 532} 533 534void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 535 checkInit(); 536 537 mDrawn = false; 538 mBounds = bounds; 539 mClip = clip; 540} 541 542void FontRenderer::finishRender() { 543 mBounds = NULL; 544 mClip = NULL; 545 546 if (mCurrentQuadIndex != 0) { 547 issueDrawCommand(); 548 mCurrentQuadIndex = 0; 549 } 550} 551 552void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) { 553 int flags = 0; 554 if (paint->isFakeBoldText()) { 555 flags |= Font::kFakeBold; 556 } 557 const float skewX = paint->getTextSkewX(); 558 uint32_t italicStyle = *(uint32_t*) &skewX; 559 const float scaleXFloat = paint->getTextScaleX(); 560 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 561 SkPaint::Style style = paint->getStyle(); 562 const float strokeWidthFloat = paint->getStrokeWidth(); 563 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 564 float fontSize = paint->getTextSize(); 565 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()), 566 fontSize, flags, italicStyle, scaleX, style, strokeWidth); 567 568 font->precache(paint, text, numGlyphs); 569} 570 571bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 572 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 573 if (!mCurrentFont) { 574 ALOGE("No font set"); 575 return false; 576 } 577 578 initRender(clip, bounds); 579 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 580 finishRender(); 581 582 return mDrawn; 583} 584 585bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 586 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 587 const float* positions, Rect* bounds) { 588 if (!mCurrentFont) { 589 ALOGE("No font set"); 590 return false; 591 } 592 593 initRender(clip, bounds); 594 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 595 finishRender(); 596 597 return mDrawn; 598} 599 600bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 601 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 602 float hOffset, float vOffset, Rect* bounds) { 603 if (!mCurrentFont) { 604 ALOGE("No font set"); 605 return false; 606 } 607 608 initRender(clip, bounds); 609 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 610 finishRender(); 611 612 return mDrawn; 613} 614 615void FontRenderer::removeFont(const Font* font) { 616 for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) { 617 if (mActiveFonts[ct] == font) { 618 mActiveFonts.removeAt(ct); 619 break; 620 } 621 } 622 623 if (mCurrentFont == font) { 624 mCurrentFont = NULL; 625 } 626} 627 628void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 629 // Compute gaussian weights for the blur 630 // e is the euler's number 631 float e = 2.718281828459045f; 632 float pi = 3.1415926535897932f; 633 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 634 // x is of the form [-radius .. 0 .. radius] 635 // and sigma varies with radius. 636 // Based on some experimental radius values and sigma's 637 // we approximately fit sigma = f(radius) as 638 // sigma = radius * 0.3 + 0.6 639 // The larger the radius gets, the more our gaussian blur 640 // will resemble a box blur since with large sigma 641 // the gaussian curve begins to lose its shape 642 float sigma = 0.3f * (float) radius + 0.6f; 643 644 // Now compute the coefficints 645 // We will store some redundant values to save some math during 646 // the blur calculations 647 // precompute some values 648 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 649 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 650 651 float normalizeFactor = 0.0f; 652 for (int32_t r = -radius; r <= radius; r ++) { 653 float floatR = (float) r; 654 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 655 normalizeFactor += weights[r + radius]; 656 } 657 658 //Now we need to normalize the weights because all our coefficients need to add up to one 659 normalizeFactor = 1.0f / normalizeFactor; 660 for (int32_t r = -radius; r <= radius; r ++) { 661 weights[r + radius] *= normalizeFactor; 662 } 663} 664 665void FontRenderer::horizontalBlur(float* weights, int32_t radius, 666 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 667 float blurredPixel = 0.0f; 668 float currentPixel = 0.0f; 669 670 for (int32_t y = 0; y < height; y ++) { 671 672 const uint8_t* input = source + y * width; 673 uint8_t* output = dest + y * width; 674 675 for (int32_t x = 0; x < width; x ++) { 676 blurredPixel = 0.0f; 677 const float* gPtr = weights; 678 // Optimization for non-border pixels 679 if (x > radius && x < (width - radius)) { 680 const uint8_t *i = input + (x - radius); 681 for (int r = -radius; r <= radius; r ++) { 682 currentPixel = (float) (*i); 683 blurredPixel += currentPixel * gPtr[0]; 684 gPtr++; 685 i++; 686 } 687 } else { 688 for (int32_t r = -radius; r <= radius; r ++) { 689 // Stepping left and right away from the pixel 690 int validW = x + r; 691 if (validW < 0) { 692 validW = 0; 693 } 694 if (validW > width - 1) { 695 validW = width - 1; 696 } 697 698 currentPixel = (float) input[validW]; 699 blurredPixel += currentPixel * gPtr[0]; 700 gPtr++; 701 } 702 } 703 *output = (uint8_t)blurredPixel; 704 output ++; 705 } 706 } 707} 708 709void FontRenderer::verticalBlur(float* weights, int32_t radius, 710 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 711 float blurredPixel = 0.0f; 712 float currentPixel = 0.0f; 713 714 for (int32_t y = 0; y < height; y ++) { 715 uint8_t* output = dest + y * width; 716 717 for (int32_t x = 0; x < width; x ++) { 718 blurredPixel = 0.0f; 719 const float* gPtr = weights; 720 const uint8_t* input = source + x; 721 // Optimization for non-border pixels 722 if (y > radius && y < (height - radius)) { 723 const uint8_t *i = input + ((y - radius) * width); 724 for (int32_t r = -radius; r <= radius; r ++) { 725 currentPixel = (float)(*i); 726 blurredPixel += currentPixel * gPtr[0]; 727 gPtr++; 728 i += width; 729 } 730 } else { 731 for (int32_t r = -radius; r <= radius; r ++) { 732 int validH = y + r; 733 // Clamp to zero and width 734 if (validH < 0) { 735 validH = 0; 736 } 737 if (validH > height - 1) { 738 validH = height - 1; 739 } 740 741 const uint8_t *i = input + validH * width; 742 currentPixel = (float) (*i); 743 blurredPixel += currentPixel * gPtr[0]; 744 gPtr++; 745 } 746 } 747 *output = (uint8_t) blurredPixel; 748 output++; 749 } 750 } 751} 752 753 754void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 755 float *gaussian = new float[2 * radius + 1]; 756 computeGaussianWeights(gaussian, radius); 757 758 uint8_t* scratch = new uint8_t[width * height]; 759 760 horizontalBlur(gaussian, radius, image, scratch, width, height); 761 verticalBlur(gaussian, radius, scratch, image, width, height); 762 763 delete[] gaussian; 764 delete[] scratch; 765} 766 767}; // namespace uirenderer 768}; // namespace android 769