FontRenderer.cpp revision ca79cf69d09efa0c327e9b1237d86a119aea5da7
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18 19#include <SkGlyph.h> 20#include <SkUtils.h> 21 22#include <cutils/properties.h> 23 24#include <utils/Log.h> 25 26#include "Caches.h" 27#include "Debug.h" 28#include "FontRenderer.h" 29#include "Rect.h" 30 31namespace android { 32namespace uirenderer { 33 34/////////////////////////////////////////////////////////////////////////////// 35// FontRenderer 36/////////////////////////////////////////////////////////////////////////////// 37 38static bool sLogFontRendererCreate = true; 39 40FontRenderer::FontRenderer() { 41 if (sLogFontRendererCreate) { 42 INIT_LOGD("Creating FontRenderer"); 43 } 44 45 mGammaTable = NULL; 46 mInitialized = false; 47 mMaxNumberOfQuads = 1024; 48 mCurrentQuadIndex = 0; 49 mLastQuadIndex = 0; 50 51 mTextMesh = NULL; 52 mCurrentCacheTexture = NULL; 53 54 mLinearFiltering = false; 55 56 mIndexBufferID = 0; 57 58 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH; 59 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT; 60 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH; 61 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT; 62 63 char property[PROPERTY_VALUE_MAX]; 64 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) { 65 mSmallCacheWidth = atoi(property); 66 } 67 68 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) { 69 mSmallCacheHeight = atoi(property); 70 } 71 72 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) { 73 mLargeCacheWidth = atoi(property); 74 } 75 76 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) { 77 mLargeCacheHeight = atoi(property); 78 } 79 80 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 81 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth; 82 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight; 83 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth; 84 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight; 85 86 if (sLogFontRendererCreate) { 87 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 88 mSmallCacheWidth, mSmallCacheHeight, 89 mLargeCacheWidth, mLargeCacheHeight >> 1, 90 mLargeCacheWidth, mLargeCacheHeight >> 1, 91 mLargeCacheWidth, mLargeCacheHeight); 92 } 93 94 sLogFontRendererCreate = false; 95} 96 97FontRenderer::~FontRenderer() { 98 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 99 delete mCacheTextures[i]; 100 } 101 mCacheTextures.clear(); 102 103 if (mInitialized) { 104 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 105 Caches::getInstance().unbindIndicesBuffer(); 106 glDeleteBuffers(1, &mIndexBufferID); 107 108 delete[] mTextMesh; 109 } 110 111 Vector<Font*> fontsToDereference = mActiveFonts; 112 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 113 delete fontsToDereference[i]; 114 } 115} 116 117void FontRenderer::flushAllAndInvalidate() { 118 if (mCurrentQuadIndex != 0) { 119 issueDrawCommand(); 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::updateDrawParams() { 324 if (mCurrentQuadIndex != mLastQuadIndex) { 325 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6)); 326 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex); 327 mDrawCacheTextures.add(mCurrentCacheTexture); 328 mLastQuadIndex = mCurrentQuadIndex; 329 } 330} 331 332void FontRenderer::checkTextureUpdate() { 333 if (!mUploadTexture) { 334 return; 335 } 336 337 Caches& caches = Caches::getInstance(); 338 GLuint lastTextureId = 0; 339 // Iterate over all the cache textures and see which ones need to be updated 340 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 341 CacheTexture* cacheTexture = mCacheTextures[i]; 342 if (cacheTexture->isDirty() && cacheTexture->getTexture()) { 343 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer 344 // of data. So expand the dirty rect to the encompassing horizontal stripe. 345 const Rect* dirtyRect = cacheTexture->getDirtyRect(); 346 uint32_t x = 0; 347 uint32_t y = dirtyRect->top; 348 uint32_t width = cacheTexture->getWidth(); 349 uint32_t height = dirtyRect->getHeight(); 350 void* textureData = cacheTexture->getTexture() + y * width; 351 352 if (cacheTexture->getTextureId() != lastTextureId) { 353 lastTextureId = cacheTexture->getTextureId(); 354 caches.activeTexture(0); 355 glBindTexture(GL_TEXTURE_2D, lastTextureId); 356 } 357#if DEBUG_FONT_RENDERER 358 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d", 359 i, x, y, width, height); 360#endif 361 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 362 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 363 cacheTexture->setDirty(false); 364 } 365 } 366 367 mUploadTexture = false; 368} 369 370void FontRenderer::issueDrawCommand() { 371 updateDrawParams(); 372 checkTextureUpdate(); 373 374 Caches& caches = Caches::getInstance(); 375 caches.bindIndicesBuffer(mIndexBufferID); 376 if (!mDrawn) { 377 float* buffer = mTextMesh; 378 int offset = 2; 379 380 bool force = caches.unbindMeshBuffer(); 381 caches.bindPositionVertexPointer(force, buffer); 382 caches.bindTexCoordsVertexPointer(force, buffer + offset); 383 } 384 385 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) { 386 uint16_t* offset = mDrawOffsets[i]; 387 uint32_t count = mDrawCounts[i]; 388 CacheTexture* texture = mDrawCacheTextures[i]; 389 390 caches.activeTexture(0); 391 glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); 392 393 texture->setLinearFiltering(mLinearFiltering, false); 394 395 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset); 396 } 397 398 mDrawn = true; 399 400 mCurrentQuadIndex = 0; 401 mLastQuadIndex = 0; 402 mDrawOffsets.clear(); 403 mDrawCounts.clear(); 404 mDrawCacheTextures.clear(); 405} 406 407void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 408 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 409 float x4, float y4, float u4, float v4, CacheTexture* texture) { 410 if (texture != mCurrentCacheTexture) { 411 updateDrawParams(); 412 // Now use the new texture id 413 mCurrentCacheTexture = texture; 414 } 415 416 const uint32_t vertsPerQuad = 4; 417 const uint32_t floatsPerVert = 4; 418 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 419 420 (*currentPos++) = x1; 421 (*currentPos++) = y1; 422 (*currentPos++) = u1; 423 (*currentPos++) = v1; 424 425 (*currentPos++) = x2; 426 (*currentPos++) = y2; 427 (*currentPos++) = u2; 428 (*currentPos++) = v2; 429 430 (*currentPos++) = x3; 431 (*currentPos++) = y3; 432 (*currentPos++) = u3; 433 (*currentPos++) = v3; 434 435 (*currentPos++) = x4; 436 (*currentPos++) = y4; 437 (*currentPos++) = u4; 438 (*currentPos++) = v4; 439 440 mCurrentQuadIndex++; 441} 442 443void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 444 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 445 float x4, float y4, float u4, float v4, CacheTexture* texture) { 446 447 if (mClip && 448 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 449 return; 450 } 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, x1); 456 mBounds->top = fmin(mBounds->top, y3); 457 mBounds->right = fmax(mBounds->right, x3); 458 mBounds->bottom = fmax(mBounds->bottom, y1); 459 } 460 461 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 462 issueDrawCommand(); 463 } 464} 465 466void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 467 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 468 float x4, float y4, float u4, float v4, CacheTexture* texture) { 469 470 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 471 472 if (mBounds) { 473 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 474 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 475 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 476 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 477 } 478 479 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 480 issueDrawCommand(); 481 } 482} 483 484void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 485 int flags = 0; 486 if (paint->isFakeBoldText()) { 487 flags |= Font::kFakeBold; 488 } 489 490 const float skewX = paint->getTextSkewX(); 491 uint32_t italicStyle = *(uint32_t*) &skewX; 492 const float scaleXFloat = paint->getTextScaleX(); 493 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 494 SkPaint::Style style = paint->getStyle(); 495 const float strokeWidthFloat = paint->getStrokeWidth(); 496 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 497 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 498 scaleX, style, strokeWidth); 499 500} 501 502FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 503 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 504 checkInit(); 505 506 if (!mCurrentFont) { 507 DropShadow image; 508 image.width = 0; 509 image.height = 0; 510 image.image = NULL; 511 image.penX = 0; 512 image.penY = 0; 513 return image; 514 } 515 516 mDrawn = false; 517 mClip = NULL; 518 mBounds = NULL; 519 520 Rect bounds; 521 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 522 523 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 524 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 525 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 526 527 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 528 dataBuffer[i] = 0; 529 } 530 531 int penX = radius - bounds.left; 532 int penY = radius - bounds.bottom; 533 534 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 535 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 536 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 537 538 DropShadow image; 539 image.width = paddedWidth; 540 image.height = paddedHeight; 541 image.image = dataBuffer; 542 image.penX = penX; 543 image.penY = penY; 544 545 return image; 546} 547 548void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 549 checkInit(); 550 551 mDrawn = false; 552 mBounds = bounds; 553 mClip = clip; 554} 555 556void FontRenderer::finishRender() { 557 mBounds = NULL; 558 mClip = NULL; 559 560 if (mCurrentQuadIndex != 0) { 561 issueDrawCommand(); 562 } 563} 564 565void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) { 566 int flags = 0; 567 if (paint->isFakeBoldText()) { 568 flags |= Font::kFakeBold; 569 } 570 const float skewX = paint->getTextSkewX(); 571 uint32_t italicStyle = *(uint32_t*) &skewX; 572 const float scaleXFloat = paint->getTextScaleX(); 573 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 574 SkPaint::Style style = paint->getStyle(); 575 const float strokeWidthFloat = paint->getStrokeWidth(); 576 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 577 float fontSize = paint->getTextSize(); 578 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()), 579 fontSize, flags, italicStyle, scaleX, style, strokeWidth); 580 581 font->precache(paint, text, numGlyphs); 582} 583 584bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 585 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 586 if (!mCurrentFont) { 587 ALOGE("No font set"); 588 return false; 589 } 590 591 initRender(clip, bounds); 592 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 593 finishRender(); 594 595 return mDrawn; 596} 597 598bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 599 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 600 const float* positions, Rect* bounds) { 601 if (!mCurrentFont) { 602 ALOGE("No font set"); 603 return false; 604 } 605 606 initRender(clip, bounds); 607 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 608 finishRender(); 609 610 return mDrawn; 611} 612 613bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 614 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 615 float hOffset, float vOffset, Rect* bounds) { 616 if (!mCurrentFont) { 617 ALOGE("No font set"); 618 return false; 619 } 620 621 initRender(clip, bounds); 622 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 623 finishRender(); 624 625 return mDrawn; 626} 627 628void FontRenderer::removeFont(const Font* font) { 629 for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) { 630 if (mActiveFonts[ct] == font) { 631 mActiveFonts.removeAt(ct); 632 break; 633 } 634 } 635 636 if (mCurrentFont == font) { 637 mCurrentFont = NULL; 638 } 639} 640 641void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 642 // Compute gaussian weights for the blur 643 // e is the euler's number 644 float e = 2.718281828459045f; 645 float pi = 3.1415926535897932f; 646 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 647 // x is of the form [-radius .. 0 .. radius] 648 // and sigma varies with radius. 649 // Based on some experimental radius values and sigma's 650 // we approximately fit sigma = f(radius) as 651 // sigma = radius * 0.3 + 0.6 652 // The larger the radius gets, the more our gaussian blur 653 // will resemble a box blur since with large sigma 654 // the gaussian curve begins to lose its shape 655 float sigma = 0.3f * (float) radius + 0.6f; 656 657 // Now compute the coefficints 658 // We will store some redundant values to save some math during 659 // the blur calculations 660 // precompute some values 661 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 662 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 663 664 float normalizeFactor = 0.0f; 665 for (int32_t r = -radius; r <= radius; r ++) { 666 float floatR = (float) r; 667 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 668 normalizeFactor += weights[r + radius]; 669 } 670 671 //Now we need to normalize the weights because all our coefficients need to add up to one 672 normalizeFactor = 1.0f / normalizeFactor; 673 for (int32_t r = -radius; r <= radius; r ++) { 674 weights[r + radius] *= normalizeFactor; 675 } 676} 677 678void FontRenderer::horizontalBlur(float* weights, int32_t radius, 679 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 680 float blurredPixel = 0.0f; 681 float currentPixel = 0.0f; 682 683 for (int32_t y = 0; y < height; y ++) { 684 685 const uint8_t* input = source + y * width; 686 uint8_t* output = dest + y * width; 687 688 for (int32_t x = 0; x < width; x ++) { 689 blurredPixel = 0.0f; 690 const float* gPtr = weights; 691 // Optimization for non-border pixels 692 if (x > radius && x < (width - radius)) { 693 const uint8_t *i = input + (x - radius); 694 for (int r = -radius; r <= radius; r ++) { 695 currentPixel = (float) (*i); 696 blurredPixel += currentPixel * gPtr[0]; 697 gPtr++; 698 i++; 699 } 700 } else { 701 for (int32_t r = -radius; r <= radius; r ++) { 702 // Stepping left and right away from the pixel 703 int validW = x + r; 704 if (validW < 0) { 705 validW = 0; 706 } 707 if (validW > width - 1) { 708 validW = width - 1; 709 } 710 711 currentPixel = (float) input[validW]; 712 blurredPixel += currentPixel * gPtr[0]; 713 gPtr++; 714 } 715 } 716 *output = (uint8_t)blurredPixel; 717 output ++; 718 } 719 } 720} 721 722void FontRenderer::verticalBlur(float* weights, int32_t radius, 723 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 724 float blurredPixel = 0.0f; 725 float currentPixel = 0.0f; 726 727 for (int32_t y = 0; y < height; y ++) { 728 uint8_t* output = dest + y * width; 729 730 for (int32_t x = 0; x < width; x ++) { 731 blurredPixel = 0.0f; 732 const float* gPtr = weights; 733 const uint8_t* input = source + x; 734 // Optimization for non-border pixels 735 if (y > radius && y < (height - radius)) { 736 const uint8_t *i = input + ((y - radius) * width); 737 for (int32_t r = -radius; r <= radius; r ++) { 738 currentPixel = (float)(*i); 739 blurredPixel += currentPixel * gPtr[0]; 740 gPtr++; 741 i += width; 742 } 743 } else { 744 for (int32_t r = -radius; r <= radius; r ++) { 745 int validH = y + r; 746 // Clamp to zero and width 747 if (validH < 0) { 748 validH = 0; 749 } 750 if (validH > height - 1) { 751 validH = height - 1; 752 } 753 754 const uint8_t *i = input + validH * width; 755 currentPixel = (float) (*i); 756 blurredPixel += currentPixel * gPtr[0]; 757 gPtr++; 758 } 759 } 760 *output = (uint8_t) blurredPixel; 761 output++; 762 } 763 } 764} 765 766 767void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 768 float *gaussian = new float[2 * radius + 1]; 769 computeGaussianWeights(gaussian, radius); 770 771 uint8_t* scratch = new uint8_t[width * height]; 772 773 horizontalBlur(gaussian, radius, image, scratch, width, height); 774 verticalBlur(gaussian, radius, scratch, image, width, height); 775 776 delete[] gaussian; 777 delete[] scratch; 778} 779 780}; // namespace uirenderer 781}; // namespace android 782