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