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