FontRenderer.cpp revision b6039811fa0fbc23f25c47491810faeb04ce3125
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 "Caches.h" 29 30namespace android { 31namespace uirenderer { 32 33/////////////////////////////////////////////////////////////////////////////// 34// Defines 35/////////////////////////////////////////////////////////////////////////////// 36 37#define DEFAULT_TEXT_CACHE_WIDTH 1024 38#define DEFAULT_TEXT_CACHE_HEIGHT 256 39#define MAX_TEXT_CACHE_WIDTH 2048 40#define TEXTURE_BORDER_SIZE 1 41 42#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) 43 44/////////////////////////////////////////////////////////////////////////////// 45// CacheTextureLine 46/////////////////////////////////////////////////////////////////////////////// 47 48bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { 49 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) { 50 return false; 51 } 52 53 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE * 2 < mMaxWidth) { 54 *retOriginX = mCurrentCol + TEXTURE_BORDER_SIZE; 55 *retOriginY = mCurrentRow + TEXTURE_BORDER_SIZE; 56 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE * 2; 57 mDirty = true; 58 return true; 59 } 60 61 return false; 62} 63 64/////////////////////////////////////////////////////////////////////////////// 65// Font 66/////////////////////////////////////////////////////////////////////////////// 67 68Font::Font(FontRenderer* state, uint32_t fontId, float fontSize, 69 int flags, uint32_t italicStyle, uint32_t scaleX, 70 SkPaint::Style style, uint32_t strokeWidth) : 71 mState(state), mFontId(fontId), mFontSize(fontSize), 72 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX), 73 mStyle(style), mStrokeWidth(mStrokeWidth) { 74} 75 76 77Font::~Font() { 78 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { 79 if (mState->mActiveFonts[ct] == this) { 80 mState->mActiveFonts.removeAt(ct); 81 break; 82 } 83 } 84 85 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 86 delete mCachedGlyphs.valueAt(i); 87 } 88} 89 90void Font::invalidateTextureCache(CacheTextureLine *cacheLine) { 91 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 92 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); 93 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) { 94 cachedGlyph->mIsValid = false; 95 } 96 } 97} 98 99void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, 100 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 101 int nPenX = x + glyph->mBitmapLeft; 102 int nPenY = y + glyph->mBitmapTop; 103 104 int width = (int) glyph->mBitmapWidth; 105 int height = (int) glyph->mBitmapHeight; 106 107 if (bounds->bottom > nPenY) { 108 bounds->bottom = nPenY; 109 } 110 if (bounds->left > nPenX) { 111 bounds->left = nPenX; 112 } 113 if (bounds->right < nPenX + width) { 114 bounds->right = nPenX + width; 115 } 116 if (bounds->top < nPenY + height) { 117 bounds->top = nPenY + height; 118 } 119} 120 121void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, 122 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 123 int nPenX = x + glyph->mBitmapLeft; 124 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 125 126 float u1 = glyph->mBitmapMinU; 127 float u2 = glyph->mBitmapMaxU; 128 float v1 = glyph->mBitmapMinV; 129 float v2 = glyph->mBitmapMaxV; 130 131 int width = (int) glyph->mBitmapWidth; 132 int height = (int) glyph->mBitmapHeight; 133 134 mState->appendMeshQuad(nPenX, nPenY, u1, v2, 135 nPenX + width, nPenY, u2, v2, 136 nPenX + width, nPenY - height, u2, v1, 137 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture); 138} 139 140void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, 141 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 142 int nPenX = x + glyph->mBitmapLeft; 143 int nPenY = y + glyph->mBitmapTop; 144 145 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth; 146 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight; 147 148 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture; 149 uint32_t cacheWidth = cacheTexture->mWidth; 150 const uint8_t* cacheBuffer = cacheTexture->mTexture; 151 152 uint32_t cacheX = 0, cacheY = 0; 153 int32_t bX = 0, bY = 0; 154 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) { 155 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) { 156#if DEBUG_FONT_RENDERER 157 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { 158 ALOGE("Skipping invalid index"); 159 continue; 160 } 161#endif 162 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; 163 bitmap[bY * bitmapW + bX] = tempCol; 164 } 165 } 166} 167 168void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, 169 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) { 170 const float halfWidth = glyph->mBitmapWidth * 0.5f; 171 const float height = glyph->mBitmapHeight; 172 173 vOffset += glyph->mBitmapTop + height; 174 175 SkPoint destination[4]; 176 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent); 177 178 // Move along the tangent and offset by the normal 179 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset, 180 -tangent->fY * halfWidth + tangent->fX * vOffset); 181 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset, 182 tangent->fY * halfWidth + tangent->fX * vOffset); 183 destination[2].set(destination[1].fX + tangent->fY * height, 184 destination[1].fY - tangent->fX * height); 185 destination[3].set(destination[0].fX + tangent->fY * height, 186 destination[0].fY - tangent->fX * height); 187 188 const float u1 = glyph->mBitmapMinU; 189 const float u2 = glyph->mBitmapMaxU; 190 const float v1 = glyph->mBitmapMinV; 191 const float v2 = glyph->mBitmapMaxV; 192 193 mState->appendRotatedMeshQuad( 194 position->fX + destination[0].fX, 195 position->fY + destination[0].fY, u1, v2, 196 position->fX + destination[1].fX, 197 position->fY + destination[1].fY, u2, v2, 198 position->fX + destination[2].fX, 199 position->fY + destination[2].fY, u2, v1, 200 position->fX + destination[3].fX, 201 position->fY + destination[3].fY, u1, v1, 202 glyph->mCachedTextureLine->mCacheTexture); 203} 204 205CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { 206 CachedGlyphInfo* cachedGlyph = NULL; 207 ssize_t index = mCachedGlyphs.indexOfKey(textUnit); 208 if (index >= 0) { 209 cachedGlyph = mCachedGlyphs.valueAt(index); 210 } else { 211 cachedGlyph = cacheGlyph(paint, textUnit); 212 } 213 214 // Is the glyph still in texture cache? 215 if (!cachedGlyph->mIsValid) { 216 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit); 217 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 218 } 219 220 return cachedGlyph; 221} 222 223void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 224 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 225 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { 226 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, 227 bitmapW, bitmapH, NULL, NULL); 228 } else { 229 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 230 0, 0, NULL, NULL); 231 } 232} 233 234void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 235 int numGlyphs, int x, int y, const float* positions) { 236 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 237 0, 0, NULL, positions); 238} 239 240void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 241 int numGlyphs, SkPath* path, float hOffset, float vOffset) { 242 if (numGlyphs == 0 || text == NULL || len == 0) { 243 return; 244 } 245 246 text += start; 247 248 int glyphsCount = 0; 249 SkFixed prevRsbDelta = 0; 250 251 float penX = 0.0f; 252 253 SkPoint position; 254 SkVector tangent; 255 256 SkPathMeasure measure(*path, false); 257 float pathLength = SkScalarToFloat(measure.getLength()); 258 259 if (paint->getTextAlign() != SkPaint::kLeft_Align) { 260 float textWidth = SkScalarToFloat(paint->measureText(text, len)); 261 float pathOffset = pathLength; 262 if (paint->getTextAlign() == SkPaint::kCenter_Align) { 263 textWidth *= 0.5f; 264 pathOffset *= 0.5f; 265 } 266 penX += pathOffset - textWidth; 267 } 268 269 while (glyphsCount < numGlyphs && penX < pathLength) { 270 glyph_t glyph = GET_GLYPH(text); 271 272 if (IS_END_OF_STRING(glyph)) { 273 break; 274 } 275 276 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 277 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 278 prevRsbDelta = cachedGlyph->mRsbDelta; 279 280 if (cachedGlyph->mIsValid) { 281 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent); 282 } 283 284 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 285 286 glyphsCount++; 287 } 288} 289 290void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 291 int numGlyphs, Rect *bounds, const float* positions) { 292 if (bounds == NULL) { 293 ALOGE("No return rectangle provided to measure text"); 294 return; 295 } 296 bounds->set(1e6, -1e6, -1e6, 1e6); 297 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions); 298} 299 300void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 301 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, 302 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { 303 if (numGlyphs == 0 || text == NULL || len == 0) { 304 return; 305 } 306 307 static RenderGlyph gRenderGlyph[] = { 308 &android::uirenderer::Font::drawCachedGlyph, 309 &android::uirenderer::Font::drawCachedGlyphBitmap, 310 &android::uirenderer::Font::measureCachedGlyph 311 }; 312 RenderGlyph render = gRenderGlyph[mode]; 313 314 text += start; 315 int glyphsCount = 0; 316 317 if (CC_LIKELY(positions == NULL)) { 318 SkFixed prevRsbDelta = 0; 319 320 float penX = x + 0.5f; 321 int penY = y; 322 323 while (glyphsCount < numGlyphs) { 324 glyph_t glyph = GET_GLYPH(text); 325 326 // Reached the end of the string 327 if (IS_END_OF_STRING(glyph)) { 328 break; 329 } 330 331 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 332 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 333 prevRsbDelta = cachedGlyph->mRsbDelta; 334 335 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 336 if (cachedGlyph->mIsValid) { 337 (*this.*render)(cachedGlyph, (int) floorf(penX), penY, 338 bitmap, bitmapW, bitmapH, bounds, positions); 339 } 340 341 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 342 343 glyphsCount++; 344 } 345 } else { 346 const SkPaint::Align align = paint->getTextAlign(); 347 348 // This is for renderPosText() 349 while (glyphsCount < numGlyphs) { 350 glyph_t glyph = GET_GLYPH(text); 351 352 // Reached the end of the string 353 if (IS_END_OF_STRING(glyph)) { 354 break; 355 } 356 357 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 358 359 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 360 if (cachedGlyph->mIsValid) { 361 int penX = x + positions[(glyphsCount << 1)]; 362 int penY = y + positions[(glyphsCount << 1) + 1]; 363 364 switch (align) { 365 case SkPaint::kRight_Align: 366 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX); 367 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY); 368 break; 369 case SkPaint::kCenter_Align: 370 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1); 371 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1); 372 default: 373 break; 374 } 375 376 (*this.*render)(cachedGlyph, penX, penY, 377 bitmap, bitmapW, bitmapH, bounds, positions); 378 } 379 380 glyphsCount++; 381 } 382 } 383} 384 385void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 386 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 387 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 388 glyph->mBitmapLeft = skiaGlyph.fLeft; 389 glyph->mBitmapTop = skiaGlyph.fTop; 390 glyph->mLsbDelta = skiaGlyph.fLsbDelta; 391 glyph->mRsbDelta = skiaGlyph.fRsbDelta; 392 393 uint32_t startX = 0; 394 uint32_t startY = 0; 395 396 // Get the bitmap for the glyph 397 paint->findImage(skiaGlyph); 398 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY); 399 400 if (!glyph->mIsValid) { 401 return; 402 } 403 404 uint32_t endX = startX + skiaGlyph.fWidth; 405 uint32_t endY = startY + skiaGlyph.fHeight; 406 407 glyph->mStartX = startX; 408 glyph->mStartY = startY; 409 glyph->mBitmapWidth = skiaGlyph.fWidth; 410 glyph->mBitmapHeight = skiaGlyph.fHeight; 411 412 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth; 413 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight; 414 415 glyph->mBitmapMinU = startX / (float) cacheWidth; 416 glyph->mBitmapMinV = startY / (float) cacheHeight; 417 glyph->mBitmapMaxU = endX / (float) cacheWidth; 418 glyph->mBitmapMaxV = endY / (float) cacheHeight; 419 420 mState->mUploadTexture = true; 421} 422 423CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { 424 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 425 mCachedGlyphs.add(glyph, newGlyph); 426 427 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph); 428 newGlyph->mGlyphIndex = skiaGlyph.fID; 429 newGlyph->mIsValid = false; 430 431 updateGlyphCache(paint, skiaGlyph, newGlyph); 432 433 return newGlyph; 434} 435 436Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize, 437 int flags, uint32_t italicStyle, uint32_t scaleX, 438 SkPaint::Style style, uint32_t strokeWidth) { 439 Vector<Font*> &activeFonts = state->mActiveFonts; 440 441 for (uint32_t i = 0; i < activeFonts.size(); i++) { 442 Font* font = activeFonts[i]; 443 if (font->mFontId == fontId && font->mFontSize == fontSize && 444 font->mFlags == flags && font->mItalicStyle == italicStyle && 445 font->mScaleX == scaleX && font->mStyle == style && 446 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) { 447 return font; 448 } 449 } 450 451 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, 452 scaleX, style, strokeWidth); 453 activeFonts.push(newFont); 454 return newFont; 455} 456 457/////////////////////////////////////////////////////////////////////////////// 458// FontRenderer 459/////////////////////////////////////////////////////////////////////////////// 460 461static bool sLogFontRendererCreate = true; 462 463FontRenderer::FontRenderer() { 464 if (sLogFontRendererCreate) { 465 INIT_LOGD("Creating FontRenderer"); 466 } 467 468 mGammaTable = NULL; 469 mInitialized = false; 470 mMaxNumberOfQuads = 1024; 471 mCurrentQuadIndex = 0; 472 473 mTextMeshPtr = NULL; 474 mCurrentCacheTexture = NULL; 475 mLastCacheTexture = NULL; 476 mCacheTextureSmall = NULL; 477 mCacheTexture128 = NULL; 478 mCacheTexture256 = NULL; 479 mCacheTexture512 = NULL; 480 481 mLinearFiltering = false; 482 483 mIndexBufferID = 0; 484 485 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 486 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 487 488 char property[PROPERTY_VALUE_MAX]; 489 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 490 if (sLogFontRendererCreate) { 491 INIT_LOGD(" Setting text cache width to %s pixels", property); 492 } 493 mSmallCacheWidth = atoi(property); 494 } else { 495 if (sLogFontRendererCreate) { 496 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth); 497 } 498 } 499 500 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 501 if (sLogFontRendererCreate) { 502 INIT_LOGD(" Setting text cache width to %s pixels", property); 503 } 504 mSmallCacheHeight = atoi(property); 505 } else { 506 if (sLogFontRendererCreate) { 507 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight); 508 } 509 } 510 511 sLogFontRendererCreate = false; 512} 513 514FontRenderer::~FontRenderer() { 515 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 516 delete mCacheLines[i]; 517 } 518 mCacheLines.clear(); 519 520 if (mInitialized) { 521 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 522 Caches::getInstance().unbindIndicesBuffer(); 523 glDeleteBuffers(1, &mIndexBufferID); 524 525 delete[] mTextMeshPtr; 526 delete mCacheTextureSmall; 527 delete mCacheTexture128; 528 delete mCacheTexture256; 529 delete mCacheTexture512; 530 } 531 532 Vector<Font*> fontsToDereference = mActiveFonts; 533 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 534 delete fontsToDereference[i]; 535 } 536} 537 538void FontRenderer::flushAllAndInvalidate() { 539 if (mCurrentQuadIndex != 0) { 540 issueDrawCommand(); 541 mCurrentQuadIndex = 0; 542 } 543 544 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 545 mActiveFonts[i]->invalidateTextureCache(); 546 } 547 548 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 549 mCacheLines[i]->mCurrentCol = 0; 550 } 551} 552 553void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) { 554 if (cacheTexture && cacheTexture->mTexture) { 555 glDeleteTextures(1, &cacheTexture->mTextureId); 556 delete[] cacheTexture->mTexture; 557 cacheTexture->mTexture = NULL; 558 cacheTexture->mTextureId = 0; 559 } 560} 561 562void FontRenderer::flushLargeCaches() { 563 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) && 564 (!mCacheTexture256 || !mCacheTexture256->mTexture) && 565 (!mCacheTexture512 || !mCacheTexture512->mTexture)) { 566 // Typical case; no large glyph caches allocated 567 return; 568 } 569 570 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 571 CacheTextureLine* cacheLine = mCacheLines[i]; 572 if ((cacheLine->mCacheTexture == mCacheTexture128 || 573 cacheLine->mCacheTexture == mCacheTexture256 || 574 cacheLine->mCacheTexture == mCacheTexture512) && 575 cacheLine->mCacheTexture->mTexture != NULL) { 576 cacheLine->mCurrentCol = 0; 577 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 578 mActiveFonts[i]->invalidateTextureCache(cacheLine); 579 } 580 } 581 } 582 583 deallocateTextureMemory(mCacheTexture128); 584 deallocateTextureMemory(mCacheTexture256); 585 deallocateTextureMemory(mCacheTexture512); 586} 587 588void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) { 589 int width = cacheTexture->mWidth; 590 int height = cacheTexture->mHeight; 591 592 cacheTexture->mTexture = new uint8_t[width * height]; 593 594 if (!cacheTexture->mTextureId) { 595 glGenTextures(1, &cacheTexture->mTextureId); 596 } 597 598 Caches::getInstance().activeTexture(0); 599 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 600 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 601 // Initialize texture dimensions 602 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, 603 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 604 605 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST; 606 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 607 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 608 609 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 610 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 611} 612 613void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 614 uint32_t* retOriginX, uint32_t* retOriginY) { 615 cachedGlyph->mIsValid = false; 616 // If the glyph is too tall, don't cache it 617 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 618 ALOGE("Font size to large to fit in cache. width, height = %i, %i", 619 (int) glyph.fWidth, (int) glyph.fHeight); 620 return; 621 } 622 623 // Now copy the bitmap into the cache texture 624 uint32_t startX = 0; 625 uint32_t startY = 0; 626 627 bool bitmapFit = false; 628 CacheTextureLine *cacheLine; 629 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 630 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 631 if (bitmapFit) { 632 cacheLine = mCacheLines[i]; 633 break; 634 } 635 } 636 637 // If the new glyph didn't fit, flush the state so far and invalidate everything 638 if (!bitmapFit) { 639 flushAllAndInvalidate(); 640 641 // Try to fit it again 642 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 643 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 644 if (bitmapFit) { 645 cacheLine = mCacheLines[i]; 646 break; 647 } 648 } 649 650 // if we still don't fit, something is wrong and we shouldn't draw 651 if (!bitmapFit) { 652 return; 653 } 654 } 655 656 cachedGlyph->mCachedTextureLine = cacheLine; 657 658 *retOriginX = startX; 659 *retOriginY = startY; 660 661 uint32_t endX = startX + glyph.fWidth; 662 uint32_t endY = startY + glyph.fHeight; 663 664 uint32_t cacheWidth = cacheLine->mMaxWidth; 665 666 CacheTexture* cacheTexture = cacheLine->mCacheTexture; 667 if (!cacheTexture->mTexture) { 668 // Large-glyph texture memory is allocated only as needed 669 allocateTextureMemory(cacheTexture); 670 } 671 672 uint8_t* cacheBuffer = cacheTexture->mTexture; 673 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 674 unsigned int stride = glyph.rowBytes(); 675 676 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 677 678 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) { 679 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0; 680 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0; 681 } 682 683 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1; 684 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) { 685 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0; 686 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0; 687 } 688 689 if (mGammaTable) { 690 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 691 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 692 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 693 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 694 } 695 } 696 } else { 697 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 698 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 699 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 700 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 701 } 702 } 703 } 704 705 cachedGlyph->mIsValid = true; 706} 707 708CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 709 CacheTexture* cacheTexture = new CacheTexture(width, height); 710 711 if (allocate) { 712 allocateTextureMemory(cacheTexture); 713 } 714 715 return cacheTexture; 716} 717 718void FontRenderer::initTextTexture() { 719 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 720 delete mCacheLines[i]; 721 } 722 mCacheLines.clear(); 723 724 if (mCacheTextureSmall) { 725 delete mCacheTextureSmall; 726 delete mCacheTexture128; 727 delete mCacheTexture256; 728 delete mCacheTexture512; 729 } 730 731 // Next, use other, separate caches for large glyphs. 732 uint16_t maxWidth = 0; 733 if (Caches::hasInstance()) { 734 maxWidth = Caches::getInstance().maxTextureSize; 735 } 736 737 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { 738 maxWidth = MAX_TEXT_CACHE_WIDTH; 739 } 740 741 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true); 742 mCacheTexture128 = createCacheTexture(maxWidth, 256, false); 743 mCacheTexture256 = createCacheTexture(maxWidth, 256, false); 744 mCacheTexture512 = createCacheTexture(maxWidth, 512, false); 745 mCurrentCacheTexture = mCacheTextureSmall; 746 747 mUploadTexture = false; 748 // Split up our default cache texture into lines of certain widths 749 int nextLine = 0; 750 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall)); 751 nextLine += mCacheLines.top()->mMaxHeight; 752 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 753 nextLine += mCacheLines.top()->mMaxHeight; 754 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 755 nextLine += mCacheLines.top()->mMaxHeight; 756 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 757 nextLine += mCacheLines.top()->mMaxHeight; 758 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 759 nextLine += mCacheLines.top()->mMaxHeight; 760 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall)); 761 nextLine += mCacheLines.top()->mMaxHeight; 762 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine, 763 nextLine, 0, mCacheTextureSmall)); 764 765 // The first cache is split into 2 lines of height 128, the rest have just one cache line. 766 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128)); 767 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128)); 768 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256)); 769 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512)); 770} 771 772// Avoid having to reallocate memory and render quad by quad 773void FontRenderer::initVertexArrayBuffers() { 774 uint32_t numIndices = mMaxNumberOfQuads * 6; 775 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 776 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 777 778 // Four verts, two triangles , six indices per quad 779 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 780 int i6 = i * 6; 781 int i4 = i * 4; 782 783 indexBufferData[i6 + 0] = i4 + 0; 784 indexBufferData[i6 + 1] = i4 + 1; 785 indexBufferData[i6 + 2] = i4 + 2; 786 787 indexBufferData[i6 + 3] = i4 + 0; 788 indexBufferData[i6 + 4] = i4 + 2; 789 indexBufferData[i6 + 5] = i4 + 3; 790 } 791 792 glGenBuffers(1, &mIndexBufferID); 793 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 794 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 795 796 free(indexBufferData); 797 798 uint32_t coordSize = 2; 799 uint32_t uvSize = 2; 800 uint32_t vertsPerQuad = 4; 801 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 802 mTextMeshPtr = new float[vertexBufferSize]; 803} 804 805// We don't want to allocate anything unless we actually draw text 806void FontRenderer::checkInit() { 807 if (mInitialized) { 808 return; 809 } 810 811 initTextTexture(); 812 initVertexArrayBuffers(); 813 814 mInitialized = true; 815} 816 817void FontRenderer::checkTextureUpdate() { 818 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { 819 return; 820 } 821 822 Caches& caches = Caches::getInstance(); 823 GLuint lastTextureId = 0; 824 // Iterate over all the cache lines and see which ones need to be updated 825 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 826 CacheTextureLine* cl = mCacheLines[i]; 827 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) { 828 CacheTexture* cacheTexture = cl->mCacheTexture; 829 uint32_t xOffset = 0; 830 uint32_t yOffset = cl->mCurrentRow; 831 uint32_t width = cl->mMaxWidth; 832 uint32_t height = cl->mMaxHeight; 833 void* textureData = cacheTexture->mTexture + (yOffset * width); 834 835 if (cacheTexture->mTextureId != lastTextureId) { 836 caches.activeTexture(0); 837 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 838 lastTextureId = cacheTexture->mTextureId; 839 } 840 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 841 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 842 843 cl->mDirty = false; 844 } 845 } 846 847 caches.activeTexture(0); 848 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); 849 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) { 850 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST; 851 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 852 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 853 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering; 854 } 855 mLastCacheTexture = mCurrentCacheTexture; 856 857 mUploadTexture = false; 858} 859 860void FontRenderer::issueDrawCommand() { 861 checkTextureUpdate(); 862 863 Caches& caches = Caches::getInstance(); 864 caches.bindIndicesBuffer(mIndexBufferID); 865 if (!mDrawn) { 866 float* buffer = mTextMeshPtr; 867 int offset = 2; 868 869 bool force = caches.unbindMeshBuffer(); 870 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 871 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 872 buffer + offset); 873 } 874 875 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 876 877 mDrawn = true; 878} 879 880void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 881 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 882 float x4, float y4, float u4, float v4, CacheTexture* texture) { 883 if (texture != mCurrentCacheTexture) { 884 if (mCurrentQuadIndex != 0) { 885 // First, draw everything stored already which uses the previous texture 886 issueDrawCommand(); 887 mCurrentQuadIndex = 0; 888 } 889 // Now use the new texture id 890 mCurrentCacheTexture = texture; 891 } 892 893 const uint32_t vertsPerQuad = 4; 894 const uint32_t floatsPerVert = 4; 895 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 896 897 (*currentPos++) = x1; 898 (*currentPos++) = y1; 899 (*currentPos++) = u1; 900 (*currentPos++) = v1; 901 902 (*currentPos++) = x2; 903 (*currentPos++) = y2; 904 (*currentPos++) = u2; 905 (*currentPos++) = v2; 906 907 (*currentPos++) = x3; 908 (*currentPos++) = y3; 909 (*currentPos++) = u3; 910 (*currentPos++) = v3; 911 912 (*currentPos++) = x4; 913 (*currentPos++) = y4; 914 (*currentPos++) = u4; 915 (*currentPos++) = v4; 916 917 mCurrentQuadIndex++; 918} 919 920void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 921 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 922 float x4, float y4, float u4, float v4, CacheTexture* texture) { 923 924 if (mClip && 925 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 926 return; 927 } 928 929 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 930 931 if (mBounds) { 932 mBounds->left = fmin(mBounds->left, x1); 933 mBounds->top = fmin(mBounds->top, y3); 934 mBounds->right = fmax(mBounds->right, x3); 935 mBounds->bottom = fmax(mBounds->bottom, y1); 936 } 937 938 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 939 issueDrawCommand(); 940 mCurrentQuadIndex = 0; 941 } 942} 943 944void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 945 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 946 float x4, float y4, float u4, float v4, CacheTexture* texture) { 947 948 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 949 950 if (mBounds) { 951 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 952 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 953 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 954 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 955 } 956 957 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 958 issueDrawCommand(); 959 mCurrentQuadIndex = 0; 960 } 961} 962 963uint32_t FontRenderer::getRemainingCacheCapacity() { 964 uint32_t remainingCapacity = 0; 965 float totalPixels = 0; 966 967 //avoid divide by zero if the size is 0 968 if (mCacheLines.size() == 0) { 969 return 0; 970 } 971 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 972 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 973 totalPixels += mCacheLines[i]->mMaxWidth; 974 } 975 remainingCapacity = (remainingCapacity * 100) / totalPixels; 976 return remainingCapacity; 977} 978 979void FontRenderer::precacheLatin(SkPaint* paint) { 980 // Remaining capacity is measured in % 981 uint32_t remainingCapacity = getRemainingCacheCapacity(); 982 uint32_t precacheIndex = 0; 983 984 // We store a string with letters in a rough frequency of occurrence 985 String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789"); 986 987 size_t size = l.size(); 988 uint16_t latin[size]; 989 paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin); 990 991 while (remainingCapacity > 25 && precacheIndex < size) { 992 mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex])); 993 remainingCapacity = getRemainingCacheCapacity(); 994 precacheIndex++; 995 } 996} 997 998void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 999 uint32_t currentNumFonts = mActiveFonts.size(); 1000 int flags = 0; 1001 if (paint->isFakeBoldText()) { 1002 flags |= Font::kFakeBold; 1003 } 1004 1005 const float skewX = paint->getTextSkewX(); 1006 uint32_t italicStyle = *(uint32_t*) &skewX; 1007 const float scaleXFloat = paint->getTextScaleX(); 1008 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 1009 SkPaint::Style style = paint->getStyle(); 1010 const float strokeWidthFloat = paint->getStrokeWidth(); 1011 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 1012 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 1013 scaleX, style, strokeWidth); 1014 1015 const float maxPrecacheFontSize = 40.0f; 1016 bool isNewFont = currentNumFonts != mActiveFonts.size(); 1017 1018 if (isNewFont && fontSize <= maxPrecacheFontSize) { 1019 precacheLatin(paint); 1020 } 1021} 1022 1023FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 1024 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 1025 checkInit(); 1026 1027 if (!mCurrentFont) { 1028 DropShadow image; 1029 image.width = 0; 1030 image.height = 0; 1031 image.image = NULL; 1032 image.penX = 0; 1033 image.penY = 0; 1034 return image; 1035 } 1036 1037 mDrawn = false; 1038 mClip = NULL; 1039 mBounds = NULL; 1040 1041 Rect bounds; 1042 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 1043 1044 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1045 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1046 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1047 1048 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1049 dataBuffer[i] = 0; 1050 } 1051 1052 int penX = radius - bounds.left; 1053 int penY = radius - bounds.bottom; 1054 1055 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1056 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 1057 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1058 1059 DropShadow image; 1060 image.width = paddedWidth; 1061 image.height = paddedHeight; 1062 image.image = dataBuffer; 1063 image.penX = penX; 1064 image.penY = penY; 1065 1066 return image; 1067} 1068 1069void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1070 checkInit(); 1071 1072 mDrawn = false; 1073 mBounds = bounds; 1074 mClip = clip; 1075} 1076 1077void FontRenderer::finishRender() { 1078 mBounds = NULL; 1079 mClip = NULL; 1080 1081 if (mCurrentQuadIndex != 0) { 1082 issueDrawCommand(); 1083 mCurrentQuadIndex = 0; 1084 } 1085} 1086 1087bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1088 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 1089 if (!mCurrentFont) { 1090 ALOGE("No font set"); 1091 return false; 1092 } 1093 1094 initRender(clip, bounds); 1095 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 1096 finishRender(); 1097 1098 return mDrawn; 1099} 1100 1101bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1102 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1103 const float* positions, Rect* bounds) { 1104 if (!mCurrentFont) { 1105 ALOGE("No font set"); 1106 return false; 1107 } 1108 1109 initRender(clip, bounds); 1110 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 1111 finishRender(); 1112 1113 return mDrawn; 1114} 1115 1116bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1117 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1118 float hOffset, float vOffset, Rect* bounds) { 1119 if (!mCurrentFont) { 1120 ALOGE("No font set"); 1121 return false; 1122 } 1123 1124 initRender(clip, bounds); 1125 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1126 finishRender(); 1127 1128 return mDrawn; 1129} 1130 1131void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1132 // Compute gaussian weights for the blur 1133 // e is the euler's number 1134 float e = 2.718281828459045f; 1135 float pi = 3.1415926535897932f; 1136 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1137 // x is of the form [-radius .. 0 .. radius] 1138 // and sigma varies with radius. 1139 // Based on some experimental radius values and sigma's 1140 // we approximately fit sigma = f(radius) as 1141 // sigma = radius * 0.3 + 0.6 1142 // The larger the radius gets, the more our gaussian blur 1143 // will resemble a box blur since with large sigma 1144 // the gaussian curve begins to lose its shape 1145 float sigma = 0.3f * (float) radius + 0.6f; 1146 1147 // Now compute the coefficints 1148 // We will store some redundant values to save some math during 1149 // the blur calculations 1150 // precompute some values 1151 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1152 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1153 1154 float normalizeFactor = 0.0f; 1155 for (int32_t r = -radius; r <= radius; r ++) { 1156 float floatR = (float) r; 1157 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1158 normalizeFactor += weights[r + radius]; 1159 } 1160 1161 //Now we need to normalize the weights because all our coefficients need to add up to one 1162 normalizeFactor = 1.0f / normalizeFactor; 1163 for (int32_t r = -radius; r <= radius; r ++) { 1164 weights[r + radius] *= normalizeFactor; 1165 } 1166} 1167 1168void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1169 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1170 float blurredPixel = 0.0f; 1171 float currentPixel = 0.0f; 1172 1173 for (int32_t y = 0; y < height; y ++) { 1174 1175 const uint8_t* input = source + y * width; 1176 uint8_t* output = dest + y * width; 1177 1178 for (int32_t x = 0; x < width; x ++) { 1179 blurredPixel = 0.0f; 1180 const float* gPtr = weights; 1181 // Optimization for non-border pixels 1182 if (x > radius && x < (width - radius)) { 1183 const uint8_t *i = input + (x - radius); 1184 for (int r = -radius; r <= radius; r ++) { 1185 currentPixel = (float) (*i); 1186 blurredPixel += currentPixel * gPtr[0]; 1187 gPtr++; 1188 i++; 1189 } 1190 } else { 1191 for (int32_t r = -radius; r <= radius; r ++) { 1192 // Stepping left and right away from the pixel 1193 int validW = x + r; 1194 if (validW < 0) { 1195 validW = 0; 1196 } 1197 if (validW > width - 1) { 1198 validW = width - 1; 1199 } 1200 1201 currentPixel = (float) input[validW]; 1202 blurredPixel += currentPixel * gPtr[0]; 1203 gPtr++; 1204 } 1205 } 1206 *output = (uint8_t)blurredPixel; 1207 output ++; 1208 } 1209 } 1210} 1211 1212void FontRenderer::verticalBlur(float* weights, int32_t radius, 1213 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1214 float blurredPixel = 0.0f; 1215 float currentPixel = 0.0f; 1216 1217 for (int32_t y = 0; y < height; y ++) { 1218 1219 uint8_t* output = dest + y * width; 1220 1221 for (int32_t x = 0; x < width; x ++) { 1222 blurredPixel = 0.0f; 1223 const float* gPtr = weights; 1224 const uint8_t* input = source + x; 1225 // Optimization for non-border pixels 1226 if (y > radius && y < (height - radius)) { 1227 const uint8_t *i = input + ((y - radius) * width); 1228 for (int32_t r = -radius; r <= radius; r ++) { 1229 currentPixel = (float)(*i); 1230 blurredPixel += currentPixel * gPtr[0]; 1231 gPtr++; 1232 i += width; 1233 } 1234 } else { 1235 for (int32_t r = -radius; r <= radius; r ++) { 1236 int validH = y + r; 1237 // Clamp to zero and width 1238 if (validH < 0) { 1239 validH = 0; 1240 } 1241 if (validH > height - 1) { 1242 validH = height - 1; 1243 } 1244 1245 const uint8_t *i = input + validH * width; 1246 currentPixel = (float) (*i); 1247 blurredPixel += currentPixel * gPtr[0]; 1248 gPtr++; 1249 } 1250 } 1251 *output = (uint8_t) blurredPixel; 1252 output ++; 1253 } 1254 } 1255} 1256 1257 1258void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1259 float *gaussian = new float[2 * radius + 1]; 1260 computeGaussianWeights(gaussian, radius); 1261 1262 uint8_t* scratch = new uint8_t[width * height]; 1263 1264 horizontalBlur(gaussian, radius, image, scratch, width, height); 1265 verticalBlur(gaussian, radius, scratch, image, width, height); 1266 1267 delete[] gaussian; 1268 delete[] scratch; 1269} 1270 1271}; // namespace uirenderer 1272}; // namespace android 1273